Logo Search packages:      
Sourcecode: kdenetwork version File versions

ircprotocol.cpp

/*
    ircprotocol - IRC Protocol

    Copyright (c) 2002      by Nick Betcher <nbetcher@kde.org>

    Kopete    (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * 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.                                   *
    *                                                                       *
    *************************************************************************
*/

#include <qregexp.h>
#include <dom/html_element.h>
#undef KDE_NO_COMPAT
#include <kaction.h>
#include <kdebug.h>
#include <klineeditdlg.h>
#include <kgenericfactory.h>
#include <ksimpleconfig.h>
#include <kiconloader.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <kcharsets.h>
#include <kuser.h>
#include <unistd.h>
#include <kmessagebox.h>

#include <qlineedit.h>
#include <qdom.h>
#include <qtimer.h>
#include <qfile.h>
#include <qpushbutton.h>
#include <qspinbox.h>
#include <qcheckbox.h>
#include <qvalidator.h>

#include "kopeteaccountmanager.h"
#include "kopetecommandhandler.h"
#include "kopeteview.h"
#include "ksparser.h"
#include "kopeteuiglobal.h"
#include "kopeteglobal.h"

#include "networkconfig.h"
#include "channellist.h"
#include "ircaccount.h"
#include "ircaddcontactpage.h"
#include "ircchannelcontact.h"
#include "irccontactmanager.h"
#include "ircguiclient.h"
#include "ircusercontact.h"
#include "irceditaccountwidget.h"
#include "irctransferhandler.h"

#include "ircprotocol.h"

typedef KGenericFactory<IRCProtocol> IRCProtocolFactory;
K_EXPORT_COMPONENT_FACTORY( kopete_irc, IRCProtocolFactory( "kopete_irc" )  )

IRCProtocol *IRCProtocol::s_protocol = 0L;

IRCProtocolHandler::IRCProtocolHandler() : Kopete::MimeTypeHandler( false )
{
      registerAsProtocolHandler( QString::fromLatin1("irc") );
}

void IRCProtocolHandler::handleURL( const KURL &url ) const
{
      kdDebug(14120) << url << endl;
      if( !url.isValid() )
            return;

      unsigned short port = url.port();
      if( port == 0 )
            port = 6667;

      QString chan = url.url().section('/',3);
      if( chan.isEmpty() )
            return;

      KUser user( getuid() );
      QString accountId = QString::fromLatin1("%1@%2:%3").arg(
            user.loginName(),
            url.host(),
            QString::number(port)
      );

      kdDebug(14120) << accountId << endl;

      IRCAccount *newAccount = new IRCAccount( IRCProtocol::protocol(), accountId, chan );
      newAccount->setNickName( user.loginName() );
      newAccount->setUserName( user.loginName() );
      newAccount->loaded();
      newAccount->connect();
}

IRCProtocol::IRCProtocol( QObject *parent, const char *name, const QStringList & /* args */ )
: KopeteProtocol( IRCProtocolFactory::instance(), parent, name ),

      m_ServerStatusOnline(KopeteOnlineStatus::Online,      90, this, 21, QString::null,  i18n("Go O&nline"),     i18n("Online")),
      m_ServerStatusOffline(KopeteOnlineStatus::Offline,    80, this, 20, QString::null,  i18n("Go O&ffline"),    i18n("Offline")),

      m_ChannelStatusOnline(KopeteOnlineStatus::Online,     70, this, 11, QString::null,  i18n("Go O&nline"),     i18n("Online")),
      m_ChannelStatusOffline(KopeteOnlineStatus::Offline,   60, this, 10, QString::null,  i18n("Go O&ffline"),    i18n("Offline")),

      m_UserStatusOp(KopeteOnlineStatus::Online,            50, this, 5, "irc_op",        i18n("Go &Op"),         i18n("Op")),
      m_UserStatusOpAway(KopeteOnlineStatus::Online,        50, this, 5, "irc_away",      i18n("Go &Op"),         i18n("Away")),
      m_UserStatusVoice(KopeteOnlineStatus::Online,         40, this, 4, "irc_voice",     i18n("Go &Voice"),      i18n("Voice")),
      m_UserStatusVoiceAway(KopeteOnlineStatus::Online,     40, this, 4, "irc_away",      i18n("Go &Voice"), i18n("Away")),
      m_UserStatusOnline(KopeteOnlineStatus::Online,        30, this, 3, QString::null,   i18n("Go O&nline"),     i18n("Online")),
      m_UserStatusAway(KopeteOnlineStatus::Away,            20, this, 2, "irc_away",      i18n("Set &Away"),      i18n("Away")),
      m_UserStatusConnecting(KopeteOnlineStatus::Connecting,      10, this, 1, "irc_connecting",      i18n("Connecting"),     i18n("Connecting")),
      m_UserStatusOffline(KopeteOnlineStatus::Offline,       0, this, 0, QString::null,   i18n("Go O&ffline"),    i18n("Offline")),

      m_StatusUnknown(KopeteOnlineStatus::Unknown, 999, this, 999, "status_unknown", "FIXME: Make this unselectable", i18n("Status not available")),

      propChannelTopic(QString::fromLatin1("channelTopic"), i18n("Topic"), QString::null, false, true ),
      propChannelMembers(QString::fromLatin1("channelMembers"), i18n("Members")),
      propHomepage(QString::fromLatin1("homePage"), i18n("Home Page")),
      propLastSeen(Kopete::Global::Properties::self()->lastSeen()),
      propUserInfo(QString::fromLatin1("userInfo"), i18n("IRC User")),
      propServer(QString::fromLatin1("ircServer"), i18n("IRC Server")),
      propChannels( QString::fromLatin1("ircChannels"), i18n("IRC Channels")),
      propHops(QString::fromLatin1("ircHops"), i18n("IRC Hops"))
{
//    kdDebug(14120) << k_funcinfo << endl;

      s_protocol = this;

      //m_status = m_unknownStatus = m_Unknown;

      addAddressBookField("messaging/irc", KopetePlugin::MakeIndexField);

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("raw"),
            SLOT( slotRawCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /raw <text> - Sends the text in raw form to the server."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quote"),
            SLOT( slotQuoteCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /quote <text> - Sends the text in quoted form to the server."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ctcp"),
            SLOT( slotCtcpCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /ctcp <nick> <message> - Send the CTCP message to nick<action>."), 2 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ping"),
            SLOT( slotPingCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /ping <nickname> - Alias for /CTCP <nickname> PING."), 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("motd"),
            SLOT( slotMotdCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /motd [<server>] - Shows the message of the day for the current or the given server.") );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("list"),
            SLOT( slotListCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /list - List the public channels on the server.") );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("join"),
            SLOT( slotJoinCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /join <#channel 1> <#channel 2...> - Joins the specified channels."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("topic"),
            SLOT( slotTopicCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /topic [<topic>] - Sets and/or displays the topic for the active channel.") );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whois"),
            SLOT( slotWhoisCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /whois <nickname> - Display whois info on this user."), 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whowas"),
            SLOT( slotWhoWasCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /whowas <nickname> - Display whowas info on this user."), 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("who"),
            SLOT( slotWhoCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /who <nickname|channel> - Display who info on this user/channel."), 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("query"),
            SLOT( slotQueryCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /query <nickname> [<message>] - Open a private chat with this user."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("mode"),
            SLOT( slotModeCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /mode <channel> <modes> - Set modes on the given channel."), 2 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("nick"),
            SLOT( slotNickCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /nick <nickname> - Change your nickname to the given one."), 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("me"),
            SLOT( slotMeCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /me <action> - Do something."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("kick"),
            SLOT( slotKickCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /kick <nickname> [<reason>] - Kick someone from the channel (requires operator status).")
            , 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ban"),
            SLOT( slotBanCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /ban <mask> - Add someone to this channel's ban list. (requires operator status)."),
            1, 1 );

      KopeteCommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("bannick"),
            QString::fromLatin1("ban %1!*@*"),
            i18n("USAGE: /bannick <nickname> - Add someone to this channel's ban list. Uses the hostmask nickname!*@* (requires operator status)."), KopeteCommandHandler::SystemAlias, 1, 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("op"),
            SLOT( slotOpCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /op <nickname 1> [<nickname 2> <...>] - Give channel operator status to someone (requires operator status)."),
            1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("deop"),
            SLOT( slotDeopCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /deop <nickname> [<nickname 2> <...>]- Remove channel operator status from someone (requires operator status)."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("voice"),
            SLOT( slotVoiceCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /voice <nickname> [<nickname 2> <...>]- Give channel voice status to someone (requires operator status)."),
            1);

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("devoice"),
            SLOT( slotDevoiceCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /devoice <nickname> [<nickname 2> <...>]- Remove channel voice status from someone (requires operator status)."), 1 );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quit"),
            SLOT( slotQuitCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /quit [<reason>] - Disconnect from IRC, optionally leaving a message.") );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("part"),
            SLOT( slotPartCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /part [<reason>] - Part from a channel, optionally leaving a message.") );

      KopeteCommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("invite"),
            SLOT( slotInviteCommand( const QString &, KopeteMessageManager*) ),
            i18n("USAGE: /invite <nickname> [<channel>] - Invite a user to join a channel."), 1 );

      KopeteCommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("j"),
            QString::fromLatin1("join %1"),
            i18n("USAGE: /j <#channel 1>, <#channel 2...> - Alias for JOIN."), KopeteCommandHandler::SystemAlias,
            1 );

      KopeteCommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("msg"),
            QString::fromLatin1("query %s"),
            i18n("USAGE: /msg <nickname> [<message>] - Alias for QUERY <nickname> <message>."), KopeteCommandHandler::SystemAlias, 1 );

      QObject::connect( KopeteMessageManagerFactory::factory(), SIGNAL(aboutToDisplay(KopeteMessage &)),
            this, SLOT(slotMessageFilter(KopeteMessage &)) );

      QObject::connect( KopeteMessageManagerFactory::factory(), SIGNAL( viewCreated( KopeteView* ) ),
            this, SLOT( slotViewCreated( KopeteView* ) ) );

      setRichTextCapabilities( KopeteProtocol::RichBFormatting | KopeteProtocol::RichUFormatting | KopeteProtocol::RichColor );

      m_commandInProgress = false;

      netConf = 0L;

      slotReadNetworks();

      m_protocolHandler = new IRCProtocolHandler();

      IRCTransferHandler::self(); // Initiate the transfer handling system.
}

IRCProtocol * IRCProtocol::protocol()
{
      return s_protocol;
}

IRCProtocol::~IRCProtocol()
{
      delete m_protocolHandler;
}

void IRCProtocol::slotViewCreated( KopeteView *view )
{
      if( view->msgManager()->protocol() == this )
            new IRCGUIClient( view->msgManager() );
}

void IRCProtocol::slotMessageFilter( KopeteMessage &msg )
{
      if( msg.from()->protocol() == this )
      {
            QString messageText = msg.escapedBody();

            //Add right click for channels, only replace text not in HTML tags
            messageText.replace( QRegExp( QString::fromLatin1("(?![^<]+>)(#[^#\\s]+)(?![^<]+>)") ), QString::fromLatin1("<span class=\"KopeteLink\" type=\"IRCChannel\">\\1</span>") );

            msg.setBody( messageText, KopeteMessage::RichText );
      }
}

00301 QPtrList<KAction> *IRCProtocol::customChatWindowPopupActions( const KopeteMessage &m, DOM::Node &n )
{
      DOM::HTMLElement e = n;

      //isNull checks that the cast was successful
      if( !e.isNull() && !m.to().isEmpty() )
      {
            activeNode = n;
            activeAccount = static_cast<IRCAccount*>( m.from()->account() );
            if( e.getAttribute( QString::fromLatin1("type") ) == QString::fromLatin1("IRCChannel") )
                  return activeAccount->contactManager()->findChannel(
                        e.innerText().string() )->customContextMenuActions();
      }

      return 0L;
}

00318 AddContactPage *IRCProtocol::createAddContactWidget(QWidget *parent, KopeteAccount *account)
{
      return new IRCAddContactPage(parent,static_cast<IRCAccount*>(account));
}

00323 KopeteEditAccountWidget *IRCProtocol::createEditAccountWidget(KopeteAccount *account, QWidget *parent)
{
      return new IRCEditAccountWidget(this, static_cast<IRCAccount*>(account),parent);
}

00328 KopeteAccount *IRCProtocol::createNewAccount(const QString &accountId)
{
      return new IRCAccount( this, accountId );
}

00333 KopeteContact *IRCProtocol::deserializeContact( KopeteMetaContact *metaContact, const QMap<QString, QString> &serializedData,
      const QMap<QString, QString> & /* addressBookData */ )
{
      kdDebug(14120) << k_funcinfo << endl;

      QString contactId = serializedData[ "contactId" ];
      QString displayName = serializedData[ "displayName" ];

      if( displayName.isEmpty() )
            displayName = contactId;

      QDict<KopeteAccount> accounts = KopeteAccountManager::manager()->accounts( this );
      if( !accounts.isEmpty() )
      {
            KopeteAccount *a = accounts[ serializedData[ "accountId" ] ];
            if( a )
            {
                  a->addContact( contactId, displayName, metaContact );
                  return a->contacts()[contactId];
            }
            else
                  kdDebug(14120) << k_funcinfo << serializedData[ "accountId" ] << " was a contact's account,"
                        " but we don't have it in the accounts list" << endl;
      }
      else
            kdDebug(14120) << k_funcinfo << "No accounts loaded!" << endl;

      return 0;
}

void IRCProtocol::slotRawCommand( const QString &args, KopeteMessageManager *manager )
{
      if( !args.isEmpty() )
      {
            static_cast<IRCAccount*>( manager->account() )->engine()->writeRawMessage( args, false );
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must enter some text to send to the server."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotQuoteCommand( const QString &args, KopeteMessageManager *manager )
{
      if( !args.isEmpty() )
      {
            static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage( args, false );
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must enter some text to send to the server."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotCtcpCommand( const QString &args, KopeteMessageManager *manager )
{
      if( !args.isEmpty() )
      {
            QString user = args.section( ' ', 0, 0 );
            QString message = args.section( ' ', 1 );
            static_cast<IRCAccount*>( manager->account() )->engine()->writeCtcpQueryMessage( user, QString::null, message );
      }
}

void IRCProtocol::slotMotdCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->motd(argsList.front());
}

void IRCProtocol::slotPingCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_pingPong(argsList.front());
}

void IRCProtocol::slotListCommand( const QString &/*args*/, KopeteMessageManager *manager )
{
      static_cast<IRCAccount*>( manager->account() )->listChannels();
}

void IRCProtocol::slotTopicCommand( const QString &args, KopeteMessageManager *manager )
{
      KopeteContactPtrList members = manager->members();
      IRCChannelContact *chan = dynamic_cast<IRCChannelContact*>( members.first() );
      if( chan )
      {
            if( !args.isEmpty() )
                  chan->setTopic( args );
            else
            {
                  static_cast<IRCAccount*>( manager->account() )->engine()->
                        writeRawMessage( QString::fromLatin1("TOPIC %1").arg( chan->nickName() ), false );
            }
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be in a channel to use this command."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotJoinCommand( const QString &args, KopeteMessageManager *manager )
{
      QString chan = KopeteCommandHandler::parseArguments( args ).front();
      if( KIRCEntity::isChannel(chan) )
      {
            static_cast<IRCAccount*>( manager->account() )->
                  contactManager()->findChannel( chan )->startChat();
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.")
                  .arg(chan), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotInviteCommand( const QString &args, KopeteMessageManager *manager )
{
      IRCChannelContact *c = 0L;
      QStringList argsList = KopeteCommandHandler::parseArguments( args );

      if( argsList.count() > 1 )
      {
            if( KIRCEntity::isChannel(argsList[1]) )
            {
                  c = static_cast<IRCAccount*>( manager->account() )->contactManager()->
                        findChannel( argsList[1] );
            }
            else
            {
                  static_cast<IRCAccount*>( manager->account() )->appendMessage(
                        i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.")
                        .arg(argsList[1]), IRCAccount::ErrorReply );
            }
      }
      else
      {
            KopeteContactPtrList members = manager->members();
            c = dynamic_cast<IRCChannelContact*>( members.first() );
      }

      if( c && c->manager()->contactOnlineStatus( manager->user() ) == m_UserStatusOp )
      {
            static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
                  QString::fromLatin1("INVITE %1 %2").arg( argsList[0] ).
                  arg( c->nickName() )
            );
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotQueryCommand( const QString &args, KopeteMessageManager *manager )
{
      QString user = args.section( ' ', 0, 0 );
      QString rest = args.section( ' ', 1 );

      if( !KIRCEntity::isChannel(user) )
      {
            IRCUserContact *c = static_cast<IRCAccount*>( manager->account() )->
                  contactManager()->findUser( user );
            c->startChat();
            if( !rest.isEmpty() )
            {
                  KopeteMessage msg( c->manager()->user(), c->manager()->members(), rest,
                        KopeteMessage::Outbound, KopeteMessage::PlainText, KopeteMessage::Chat);
                  c->manager()->sendMessage(msg);
            }
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("\"%1\" is an invalid nickname. Nicknames must not start with '#','!','+', or '&'.").arg(user),
                  IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotWhoisCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->whoisUser( argsList.first() );
      m_commandInProgress = true;
}

void IRCProtocol::slotWhoCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
            QString::fromLatin1("WHO %1").arg( argsList.first() ) );
      m_commandInProgress = true;
}

void IRCProtocol::slotWhoWasCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
            QString::fromLatin1("WHOWAS %1").arg( argsList.first() ) );
      m_commandInProgress = true;
}

void IRCProtocol::slotQuitCommand( const QString &args, KopeteMessageManager *manager )
{
      static_cast<IRCAccount*>( manager->account() )->quit( args );
}

void IRCProtocol::slotNickCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->changeNickname( argsList.front() );
}

void IRCProtocol::slotModeCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->changeMode( argsList.front(),
            args.section( QRegExp(QString::fromLatin1("\\s+")), 1 ) );
}

void IRCProtocol::slotMeCommand( const QString &args, KopeteMessageManager *manager )
{
      KopeteContactPtrList members = manager->members();
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_action(
            static_cast<const IRCContact*>(members.first())->nickName(), args );
}

void IRCProtocol::slotKickCommand( const QString &args, KopeteMessageManager *manager )
{
      if( manager->contactOnlineStatus( manager->user() ) == m_UserStatusOp )
      {
            QRegExp spaces(QString::fromLatin1("\\s+"));
            QString nick = args.section( spaces, 0, 0);
            QString reason = args.section( spaces, 1);
            KopeteContactPtrList members = manager->members();
            QString channel = static_cast<IRCContact*>( members.first() )->nickName();
            if( KIRCEntity::isChannel(channel) )
                  static_cast<IRCAccount*>( manager->account() )->engine()->kickUser( nick, channel, reason );
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotBanCommand( const QString &args, KopeteMessageManager *manager )
{
      if( manager->contactOnlineStatus( manager->user() ) == m_UserStatusOp )
      {
            QStringList argsList = KopeteCommandHandler::parseArguments( args );
            KopeteContactPtrList members = manager->members();
            IRCChannelContact *chan = static_cast<IRCChannelContact*>( members.first() );
            if( chan && chan->locateUser( argsList.front() ) )
                  chan->setMode( QString::fromLatin1("+b %1").arg( argsList.front() ) );
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotPartCommand( const QString &args, KopeteMessageManager *manager )
{
      QStringList argsList = KopeteCommandHandler::parseArguments( args );
      KopeteContactPtrList members = manager->members();
      IRCChannelContact *chan = static_cast<IRCChannelContact*>( members.first() );

      if( chan )
      {
            if( !args.isEmpty() )
                  static_cast<IRCAccount*>( manager->account() )->engine()->partChannel(chan->nickName(), args);
            else
                  chan->part();
            manager->view()->closeView();
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be in a channel to use this command."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::slotOpCommand( const QString &args, KopeteMessageManager *manager )
{
      simpleModeChange( args, manager, QString::fromLatin1("+o") );
}

void IRCProtocol::slotDeopCommand( const QString &args, KopeteMessageManager *manager )
{
      simpleModeChange( args, manager, QString::fromLatin1("-o") );
}

void IRCProtocol::slotVoiceCommand( const QString &args, KopeteMessageManager *manager )
{
      simpleModeChange( args, manager, QString::fromLatin1("+v") );
}

void IRCProtocol::slotDevoiceCommand( const QString &args, KopeteMessageManager *manager )
{
      simpleModeChange( args, manager, QString::fromLatin1("-v") );
}

void IRCProtocol::simpleModeChange( const QString &args, KopeteMessageManager *manager, const QString &mode )
{
      if( manager->contactOnlineStatus( manager->user() ) == m_UserStatusOp )
      {
            QStringList argsList = KopeteCommandHandler::parseArguments( args );
            KopeteContactPtrList members = manager->members();
            IRCChannelContact *chan = static_cast<IRCChannelContact*>( members.first() );
            if( chan )
            {
                  for( QStringList::iterator it = argsList.begin(); it != argsList.end(); ++it )
                  {
                        if( chan->locateUser( *it ) )
                              chan->setMode( QString::fromLatin1("%1 %2").arg( mode ).arg( *it ) );
                  }
            }
      }
      else
      {
            static_cast<IRCAccount*>( manager->account() )->appendMessage(
                  i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
      }
}

void IRCProtocol::editNetworks( const QString &networkName )
{
      if( !netConf )
      {
            netConf = new NetworkConfig( Kopete::UI::Global::mainWidget(), "network_config", true );
            netConf->host->setValidator( new QRegExpValidator( QString::fromLatin1("^[\\w-\\.]*$"), netConf ) );
            netConf->upButton->setPixmap( SmallIcon( "up" )  );
            netConf->downButton->setPixmap( SmallIcon( "down" ) );

            connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
            connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
            connect( netConf, SIGNAL( accepted() ), this, SLOT( slotSaveNetworkConfig() ) );
            connect( netConf, SIGNAL( rejected() ), this, SLOT( slotReadNetworks() ) );
            connect( netConf->upButton, SIGNAL( clicked() ), this, SLOT( slotMoveServerUp() ) );
            connect( netConf->downButton, SIGNAL( clicked() ), this, SLOT( slotMoveServerDown() ) );
            connect( netConf->removeNetwork, SIGNAL( clicked() ), this, SLOT( slotDeleteNetwork() ) );
            connect( netConf->removeHost, SIGNAL( clicked() ), this, SLOT( slotDeleteHost() ) );
            connect( netConf->newHost, SIGNAL( clicked() ), this, SLOT( slotNewHost() ) );
            connect( netConf->newNetwork, SIGNAL( clicked() ), this, SLOT( slotNewNetwork() ) );
            connect( netConf->renameNetwork, SIGNAL( clicked() ), this, SLOT( slotRenameNetwork() ) );
            connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
      }

      disconnect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
      disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );

      netConf->networkList->clear();

      for( QDictIterator<IRCNetwork> it( m_networks ); it.current(); ++it )
      {
            IRCNetwork *net = it.current();
            netConf->networkList->insertItem( net->name );
      }

      netConf->networkList->sort();

      connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
      connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );

      if( !networkName.isEmpty() )
            netConf->networkList->setSelected( netConf->networkList->findItem( networkName ), true );

      //slotUpdateNetworkConfig(); // unnecessary, setSelected emits selectionChanged

      netConf->show();
}

void IRCProtocol::slotUpdateNetworkConfig()
{
      // update the data structure of the previous selection from the UI
      storeCurrentNetwork();

      // update the UI from the data for the current selection
      IRCNetwork *net = m_networks[ netConf->networkList->currentText() ];
      if( net )
      {
            netConf->description->setText( net->description );
            netConf->hostList->clear();

            for( QValueList<IRCHost*>::iterator it = net->hosts.begin(); it != net->hosts.end(); ++it )
                  netConf->hostList->insertItem( (*it)->host + QString::fromLatin1(":") + QString::number((*it)->port) );

            // prevent nested event loop crash
            disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
            netConf->hostList->setSelected( 0, true );
            slotUpdateNetworkHostConfig();
            connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
      }
      
      // record the current selection
      m_uiCurrentNetworkSelection = netConf->networkList->currentText();
}

void IRCProtocol::storeCurrentNetwork()
{
      if ( !m_uiCurrentNetworkSelection.isEmpty() )
      {
            IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
            if ( net )
            {
                  net->description = netConf->description->text(); // crash on 2nd dialog show here!
            }
            else
                  kdDebug( 14020 ) << m_uiCurrentNetworkSelection << " was already gone from the cache!" << endl;
      }
}

void IRCProtocol::storeCurrentHost()
{
      if ( !m_uiCurrentHostSelection.isEmpty()  )
      {
            IRCHost *host = m_hosts[ m_uiCurrentHostSelection ];
            if ( host )
            {
                  host->host = netConf->host->text();
                  host->password = netConf->password->text();
                  host->port = netConf->port->text().toInt();
                  host->ssl = netConf->useSSL->isChecked();
            }
      }
}

void IRCProtocol::slotHostPortChanged( int value )
{
      QString entryText = m_uiCurrentHostSelection + QString::fromLatin1(":") + QString::number( value );
      // changeItem causes a take() and insert, and we don't want a selectionChanged() signal that sets all this off again.
      disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
      netConf->hostList->changeItem( entryText, netConf->hostList->currentItem() );
      connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
}

void IRCProtocol::slotUpdateNetworkHostConfig()
{
      storeCurrentHost();

      if ( netConf->hostList->selectedItem() )
      {
            m_uiCurrentHostSelection = netConf->hostList->currentText().section(':', 0, 0);
            IRCHost *host = m_hosts[ m_uiCurrentHostSelection ];

            if( host )
            {
                  netConf->host->setText( host->host );
                  netConf->password->setText( host->password );
                  netConf->port->setValue( host->port );
                  netConf->useSSL->setChecked( host->ssl );
            }
      }
      else
      {
            m_uiCurrentHostSelection = QString();
            disconnect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
            netConf->host->clear();
            netConf->password->clear();
            netConf->port->setValue( 6667 );
            netConf->useSSL->setChecked( false );
            connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
      }
}

void IRCProtocol::slotDeleteNetwork()
{
      QString network = netConf->networkList->currentText();
      if( KMessageBox::warningContinueCancel(
            Kopete::UI::Global::mainWidget(), i18n("<qt>Are you sure you want to delete the network <b>%1</b>?<br>"
            "Any accounts which use this network will have to be modified.</qt>")
            .arg(network), i18n("Deleting Network"),
            KGuiItem(i18n("&Delete Network"),"editdelete"), QString::fromLatin1("AskIRCDeleteNetwork") ) == KMessageBox::Continue )
      {
            IRCNetwork *net = m_networks[ network ];
            for( QValueList<IRCHost*>::iterator it = net->hosts.begin(); it != net->hosts.end(); ++it )
            {
                  m_hosts.remove( (*it)->host );
                  delete (*it);
            }
            m_networks.remove( network );
            delete net;
            netConf->networkList->removeItem( netConf->networkList->currentItem() );
            slotUpdateNetworkHostConfig();
            
      }
}

void IRCProtocol::slotDeleteHost()
{
      QString hostName = netConf->host->text();
      if ( KMessageBox::warningContinueCancel(
            Kopete::UI::Global::mainWidget(), i18n("<qt>Are you sure you want to delete the host <b>%1</b>?</qt>")
            .arg(hostName), i18n("Deleting Host"),
            KGuiItem(i18n("&Delete Host"),"editdelete"), QString::fromLatin1("AskIRCDeleteHost")) == KMessageBox::Continue )
      {
            IRCHost *host = m_hosts[ hostName ];
            if ( host )
            {
                  disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
                  QString entryText = host->host + QString::fromLatin1(":") + QString::number(host->port);
                  QListBoxItem * justAdded = netConf->hostList->findItem( entryText );
                  netConf->hostList->removeItem( netConf->hostList->index( justAdded ) );
                  connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
                  
                  // remove from network as well
                  IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
                  net->hosts.remove( host );
                  
                  m_hosts.remove( host->host );
                  delete host;
            }
      }
}

void IRCProtocol::slotNewNetwork()
{ 
      // create a new network struct
      IRCNetwork *net = new IRCNetwork;
      // give it the name of 'New Network' (incrementing number if needed) 
      QString netName = QString::fromLatin1( "New Network" );
      if ( m_networks.find( netName ) )
      {
            int newIdx = 1;
            do {
                  netName = QString::fromLatin1( "New Network #%1" ).arg( newIdx++ );
            }
            while ( m_networks.find( netName ) && newIdx < 100 );
            if ( newIdx == 100 ) // pathological case
                  return;
      }
      net->name = netName;
      // and add it to the networks dict and list
      m_networks.insert( net->name, net );
      netConf->networkList->insertItem( net->name );
      QListBoxItem * justAdded = netConf->networkList->findItem( net->name );
      netConf->networkList->setSelected( justAdded, true );
      netConf->networkList->setBottomItem( netConf->networkList->index( justAdded ) );
}

void IRCProtocol::slotNewHost()
{
      // create a new host
      IRCHost *host = new IRCHost;
      // prompt for a name
      bool ok;
      QString name = KLineEditDlg::getText(i18n("New Host"), i18n("Enter the hostname of the new server:"), QString::null, &ok, Kopete::UI::Global::mainWidget() );
      if ( ok )
      {
            // dupe check
            if ( m_hosts[ name ] )
            {
                  KMessageBox::sorry(netConf, i18n( "A host already exists with that name" ) );
                  return;
            }
            // set defaults on others
            host->host = name;
            host->port = 6667;
            host->ssl = false;
            // add it to the dict
            m_hosts.insert( host->host, host );
            // add it to the network!
            IRCNetwork *net = m_networks[ netConf->networkList->currentText() ];
            net->hosts.append( host );
            // add it to the gui 
            QString entryText = host->host + QString::fromLatin1(":") + QString::number(host->port);
            netConf->hostList->insertItem( entryText );
            // select it in the gui
            QListBoxItem * justAdded = netConf->hostList->findItem( entryText );
            netConf->hostList->setSelected( justAdded, true );
            //netConf->hostList->setBottomItem( netConf->hostList->index( justAdded ) );
      }
}

void IRCProtocol::slotRenameNetwork()
{
      IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
      if ( net )
      {
            bool ok;
            // popup up a dialog containing the current name
            QString name = KLineEditDlg::getText(i18n("Rename Network"), i18n("Enter the new name for this network:"), m_uiCurrentNetworkSelection, &ok, Kopete::UI::Global::mainWidget() );
            if ( ok )
            {
                  if ( m_uiCurrentNetworkSelection != name )
                  {
                        // dupe check
                        if ( m_networks[ name ] )
                        {
                              KMessageBox::sorry(netConf, i18n( "A network already exists with that name" ) );
                              return;
                        }

                        net->name = name;
                        // dict
                        m_networks.remove( m_uiCurrentNetworkSelection );
                        m_networks.insert( net->name, net );
                        // ui
                        int idx = netConf->networkList->index( netConf->networkList->findItem( m_uiCurrentNetworkSelection ) );
                        m_uiCurrentNetworkSelection = net->name;
                        netConf->networkList->changeItem( net->name, idx ); // changes the selection!!!
                        netConf->networkList->sort();
                  }
            }
      } 
}

void IRCProtocol::addNetwork( IRCNetwork *network )
{
      m_networks.insert( network->name, network );
      slotSaveNetworkConfig();
}

void IRCProtocol::slotSaveNetworkConfig()
{
      // store any changes in the UI
      storeCurrentNetwork();
      kdDebug( 14120 ) <<  k_funcinfo << m_uiCurrentHostSelection << endl;
      storeCurrentHost();
      
      QDomDocument doc("irc-networks");
      QDomNode root = doc.appendChild( doc.createElement("networks") );

      for( QDictIterator<IRCNetwork> it( m_networks ); it.current(); ++it )
      {
            IRCNetwork *net = it.current();

            QDomNode networkNode = root.appendChild( doc.createElement("network") );
            QDomNode nameNode = networkNode.appendChild( doc.createElement("name") );
            nameNode.appendChild( doc.createTextNode( net->name ) );

            QDomNode descNode = networkNode.appendChild( doc.createElement("description") );
            descNode.appendChild( doc.createTextNode( net->description ) );

            QDomNode serversNode = networkNode.appendChild( doc.createElement("servers") );

            for( QValueList<IRCHost*>::iterator it2 = net->hosts.begin(); it2 != net->hosts.end(); it2++ )
            {
                  QDomNode serverNode = serversNode.appendChild( doc.createElement("server") );

                  QDomNode hostNode = serverNode.appendChild( doc.createElement("host") );
                  hostNode.appendChild( doc.createTextNode( (*it2)->host ) );

                  QDomNode portNode = serverNode.appendChild( doc.createElement("port" ) );
                  portNode.appendChild( doc.createTextNode( QString::number( (*it2)->port ) ) );

                  QDomNode sslNode = serverNode.appendChild( doc.createElement("useSSL") );
                  sslNode.appendChild( doc.createTextNode( (*it2)->ssl ? "true" : "false" ) );
            }
      }

      kdDebug(14121) << k_funcinfo << doc.toString(4) << endl;
      QFile xmlFile( locateLocal( "appdata", "ircnetworks.xml" ) );
      QTextStream stream( &xmlFile );

      xmlFile.open( IO_WriteOnly );
      stream << doc.toString(4);
      xmlFile.close();

      if (netConf)
            emit networkConfigUpdated( netConf->networkList->currentText() );
}

void IRCProtocol::slotReadNetworks()
{
      m_networks.clear();
      m_hosts.clear();

      QFile xmlFile( locate( "appdata", "ircnetworks.xml" ) );
      xmlFile.open( IO_ReadOnly );

      QDomDocument doc;
      doc.setContent( &xmlFile );
      QDomElement networkNode = doc.documentElement().firstChild().toElement();
      while( !networkNode.isNull () )
      {
            IRCNetwork *net = new IRCNetwork;

            QDomElement networkChild = networkNode.firstChild().toElement();
            while( !networkChild.isNull() )
            {
                  if( networkChild.tagName() == "name" )
                        net->name = networkChild.text();
                  else if( networkChild.tagName() == "description" )
                        net->description = networkChild.text();
                  else if( networkChild.tagName() == "servers" )
                  {
                        QDomElement server = networkChild.firstChild().toElement();
                        while( !server.isNull() )
                        {
                              IRCHost *host = new IRCHost;

                              QDomElement serverChild = server.firstChild().toElement();
                              while( !serverChild.isNull() )
                              {
                                    if( serverChild.tagName() == "host" )
                                          host->host = serverChild.text();
                                    else if( serverChild.tagName() == "port" )
                                          host->port = serverChild.text().toInt();
                                    else if( serverChild.tagName() == "useSSL" )
                                          host->ssl = ( serverChild.text() == "true" );

                                    serverChild = serverChild.nextSibling().toElement();
                              }

                              net->hosts.append( host );
                              m_hosts.insert( host->host, host );
                              server = server.nextSibling().toElement();
                        }
                  }
                  networkChild = networkChild.nextSibling().toElement();
            }

            m_networks.insert( net->name, net );
            networkNode = networkNode.nextSibling().toElement();
      }

      xmlFile.close();
}

void IRCProtocol::slotMoveServerUp()
{
      IRCHost *selectedHost = m_hosts[ netConf->hostList->currentText().section(':', 0, 0) ];
      IRCNetwork *selectedNetwork = m_networks[ netConf->networkList->currentText() ];

      if( !selectedNetwork || !selectedHost )
            return;

      QValueList<IRCHost*>::iterator pos = selectedNetwork->hosts.find( selectedHost );
      if( pos != selectedNetwork->hosts.begin() )
      {
            QValueList<IRCHost*>::iterator lastPos = pos;
            lastPos--;
            selectedNetwork->hosts.insert( lastPos, selectedHost );
            selectedNetwork->hosts.remove( pos );
      }

      unsigned int currentPos = netConf->hostList->currentItem();
      if( currentPos > 0 )
      {
            netConf->hostList->removeItem( currentPos );
            kdDebug(14121) << k_funcinfo << selectedHost->host << endl;
            netConf->hostList->insertItem( selectedHost->host, --currentPos );
            netConf->hostList->setSelected( currentPos, true );
      }
}

void IRCProtocol::slotMoveServerDown()
{
      IRCHost *selectedHost = m_hosts[ netConf->hostList->currentText().section(':', 0, 0) ];
      IRCNetwork *selectedNetwork = m_networks[ netConf->networkList->currentText() ];

      if( !selectedNetwork || !selectedHost )
            return;

      QValueList<IRCHost*>::iterator pos = selectedNetwork->hosts.find( selectedHost );
      if( *pos != selectedNetwork->hosts.back() )
      {
            QValueList<IRCHost*>::iterator nextPos = pos;
            nextPos++;
            selectedNetwork->hosts.insert( nextPos, selectedHost );
            selectedNetwork->hosts.remove( pos );
      }

      unsigned int currentPos = netConf->hostList->currentItem();
      if( currentPos < ( netConf->hostList->count() - 1 ) )
      {
            netConf->hostList->removeItem( currentPos );
            netConf->hostList->insertItem( selectedHost->host, ++currentPos );
            netConf->hostList->setSelected( currentPos, true );
      }
}



#include "ircprotocol.moc"

// vim: set noet ts=4 sts=4 sw=4:

Generated by  Doxygen 1.6.0   Back to index