You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/irc/ircprotocol.cpp

1242 lines
44 KiB

/*
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 "ircaccount.h"
#include "ircprotocol.h"
#include "ksparser.h"
#include "ircaddcontactpage.h"
#include "ircchannelcontact.h"
#include "irccontactmanager.h"
#include "networkconfig.h"
#include "channellist.h"
#include "ircguiclient.h"
#include "ircusercontact.h"
#include "irceditaccountwidget.h"
#include "irctransferhandler.h"
#include "kircengine.h"
#include "kopeteaccountmanager.h"
#include "kopetecommandhandler.h"
#include "kopeteglobal.h"
#include "kopeteonlinestatusmanager.h"
#include "kopeteonlinestatus.h"
#include "kopeteview.h"
#include "kopeteuiglobal.h"
#undef KDE_NO_COMPAT
#include <kaction.h>
#include <kcharsets.h>
#include <kdebug.h>
#include <kgenericfactory.h>
#include <kglobal.h>
#include <kinputdialog.h>
#include <kiconloader.h>
#include <kmessagebox.h>
#include <ksimpleconfig.h>
#include <kstandarddirs.h>
#include <kuser.h>
#include <qcheckbox.h>
#include <qdom.h>
#include <qfile.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qregexp.h>
#include <qspinbox.h>
#include <qvalidator.h>
#include <dom/html_element.h>
#include <unistd.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->connect();
}
IRCProtocol::IRCProtocol( QObject *parent, const char *name, const QStringList & /* args */ )
: Kopete::Protocol( IRCProtocolFactory::instance(), parent, name ),
m_ServerStatusOnline(Kopete::OnlineStatus::Online,
100, this, OnlineServer, QString::null, i18n("Online")),
m_ServerStatusOffline(Kopete::OnlineStatus::Offline,
90, this, OfflineServer, QString::null, i18n("Offline")),
m_ChannelStatusOnline(Kopete::OnlineStatus::Online,
80, this, OnlineChannel, QString::null, i18n("Online")),
m_ChannelStatusOffline(Kopete::OnlineStatus::Offline,
70, this, OfflineChannel, QString::null, i18n("Offline")),
m_UserStatusOpVoice(Kopete::OnlineStatus::Online,
60, this, Online | Operator | Voiced, QStringList::split(' ',"irc_voice irc_op"), i18n("Op")),
m_UserStatusOpVoiceAway(Kopete::OnlineStatus::Away,
55, this, Online | Operator | Voiced | Away,
QStringList::split(' ',"irc_voice irc_op contact_away_overlay"), i18n("Away")),
m_UserStatusOp(Kopete::OnlineStatus::Online,
50, this, Online | Operator, "irc_op", i18n("Op")),
m_UserStatusOpAway(Kopete::OnlineStatus::Away,
45, this, Online | Operator | Away,
QStringList::split(' ',"irc_op contact_away_overlay"), i18n("Away")),
m_UserStatusVoice(Kopete::OnlineStatus::Online,
40, this, Online | Voiced, "irc_voice", i18n("Voice")),
m_UserStatusVoiceAway(Kopete::OnlineStatus::Away,
35, this, Online | Voiced | Away,
QStringList::split(' ',"irc_voice contact_away_overlay"), i18n("Away")),
m_UserStatusOnline(Kopete::OnlineStatus::Online,
25, this, Online, QString::null, i18n("Online"), i18n("Online"), Kopete::OnlineStatusManager::Online),
m_UserStatusAway(Kopete::OnlineStatus::Away,
2, this, Online | Away, "contact_away_overlay",
i18n("Away"), i18n("Away"), Kopete::OnlineStatusManager::Away),
m_UserStatusConnecting(Kopete::OnlineStatus::Connecting,
1, this, Connecting, "irc_connecting", i18n("Connecting")),
m_UserStatusOffline(Kopete::OnlineStatus::Offline,
0, this, Offline, QString::null, i18n("Offline"), i18n("Offline"), Kopete::OnlineStatusManager::Offline),
m_StatusUnknown(Kopete::OnlineStatus::Unknown,
999, this, 999, "status_unknown", 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")),
propFullName(QString::fromLatin1("FormattedName"), i18n("Full Name")),
propIsIdentified(QString::fromLatin1("identifiedUser"), i18n("User Is Authenticated"))
{
// kdDebug(14120) << k_funcinfo << endl;
s_protocol = this;
//m_status = m_unknownStatus = m_Unknown;
addAddressBookField("messaging/irc", Kopete::Plugin::MakeIndexField);
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("raw"),
SLOT( slotRawCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /raw <text> - Sends the text in raw form to the server."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quote"),
SLOT( slotQuoteCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /quote <text> - Sends the text in quoted form to the server."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ctcp"),
SLOT( slotCtcpCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /ctcp <nick> <message> - Send the CTCP message to nick<action>."), 2 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ping"),
SLOT( slotPingCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /ping <nickname> - Alias for /CTCP <nickname> PING."), 1, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("motd"),
SLOT( slotMotdCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /motd [<server>] - Shows the message of the day for the current or the given server.") );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("list"),
SLOT( slotListCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /list - List the public channels on the server.") );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("join"),
SLOT( slotJoinCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /join <#channel 1> [<password>] - Joins the specified channel."), 1, 2 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("topic"),
SLOT( slotTopicCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /topic [<topic>] - Sets and/or displays the topic for the active channel.") );
//FIXME: Update help text
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whois"),
SLOT( slotWhoisCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /whois <nickname> - Display whois info on this user."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whowas"),
SLOT( slotWhoWasCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /whowas <nickname> - Display whowas info on this user."), 1, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("who"),
SLOT( slotWhoCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /who <nickname|channel> - Display who info on this user/channel."), 1, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("query"),
SLOT( slotQueryCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /query <nickname> [<message>] - Open a private chat with this user."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("mode"),
SLOT( slotModeCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /mode <channel> <modes> - Set modes on the given channel."), 2 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("nick"),
SLOT( slotNickCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /nick <nickname> - Change your nickname to the given one."), 1, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("me"),
SLOT( slotMeCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /me <action> - Do something."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ame"),
SLOT( slotAllMeCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /ame <action> - Do something in every open chat."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("kick"),
SLOT( slotKickCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /kick <nickname> [<reason>] - Kick someone from the channel (requires operator status).")
, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ban"),
SLOT( slotBanCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /ban <mask> - Add someone to this channel's ban list. (requires operator status)."),
1, 1 );
Kopete::CommandHandler::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)."), Kopete::CommandHandler::SystemAlias, 1, 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("op"),
SLOT( slotOpCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /op <nickname 1> [<nickname 2> <...>] - Give channel operator status to someone (requires operator status)."),
1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("deop"),
SLOT( slotDeopCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /deop <nickname> [<nickname 2> <...>]- Remove channel operator status from someone (requires operator status)."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("voice"),
SLOT( slotVoiceCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /voice <nickname> [<nickname 2> <...>]- Give channel voice status to someone (requires operator status)."),
1);
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("devoice"),
SLOT( slotDevoiceCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /devoice <nickname> [<nickname 2> <...>]- Remove channel voice status from someone (requires operator status)."), 1 );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quit"),
SLOT( slotQuitCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /quit [<reason>] - Disconnect from IRC, optionally leaving a message.") );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("part"),
SLOT( slotPartCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /part [<reason>] - Part from a channel, optionally leaving a message.") );
Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("invite"),
SLOT( slotInviteCommand( const QString &, Kopete::ChatSession*) ),
i18n("USAGE: /invite <nickname> [<channel>] - Invite a user to join a channel."), 1 );
Kopete::CommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("j"),
QString::fromLatin1("join %1"),
i18n("USAGE: /j <#channel 1> [<password>] - Alias for JOIN."), Kopete::CommandHandler::SystemAlias,
1, 2 );
Kopete::CommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("msg"),
QString::fromLatin1("query %s"),
i18n("USAGE: /msg <nickname> [<message>] - Alias for QUERY <nickname> <message>."), Kopete::CommandHandler::SystemAlias, 1 );
QObject::connect( Kopete::ChatSessionManager::self(), SIGNAL(aboutToDisplay(Kopete::Message &)),
this, SLOT(slotMessageFilter(Kopete::Message &)) );
QObject::connect( Kopete::ChatSessionManager::self(), SIGNAL( viewCreated( KopeteView* ) ),
this, SLOT( slotViewCreated( KopeteView* ) ) );
setCapabilities( Kopete::Protocol::RichBFormatting | Kopete::Protocol::RichUFormatting | Kopete::Protocol::RichColor );
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;
}
const Kopete::OnlineStatus IRCProtocol::statusLookup( IRCStatus status ) const
{
kdDebug(14120) << k_funcinfo << "Looking up status for " << status << endl;
switch( status )
{
case Offline:
return m_UserStatusOffline;
case Connecting:
return m_UserStatusConnecting;
// Regular user
case Online:
return m_UserStatusOnline;
case Online | Away:
return m_UserStatusAway;
// Voiced
case Online | Voiced:
return m_UserStatusVoice;
case Online | Away | Voiced:
return m_UserStatusVoiceAway;
// Operator
case Online | Operator:
return m_UserStatusOp;
case Online | Away | Operator:
return m_UserStatusOpAway;
case Online | Operator | Voiced:
return m_UserStatusOpVoice;
case Online | Operator | Voiced | Away:
return m_UserStatusOpVoiceAway;
// Server
case OnlineServer:
return m_ServerStatusOnline;
case OfflineServer:
return m_ServerStatusOffline;
// Channel
case OnlineChannel:
return m_ChannelStatusOnline;
case OfflineChannel:
return m_ChannelStatusOffline;
default:
return m_StatusUnknown;
}
}
void IRCProtocol::slotViewCreated( KopeteView *view )
{
if( view->msgManager()->protocol() == this )
new IRCGUIClient( view->msgManager() );
}
void IRCProtocol::slotMessageFilter( Kopete::Message &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, Kopete::Message::RichText );
}
}
QPtrList<KAction> *IRCProtocol::customChatWindowPopupActions( const Kopete::Message &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;
}
AddContactPage *IRCProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account)
{
return new IRCAddContactPage(parent,static_cast<IRCAccount*>(account));
}
KopeteEditAccountWidget *IRCProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
{
return new IRCEditAccountWidget(this, static_cast<IRCAccount*>(account),parent);
}
Kopete::Account *IRCProtocol::createNewAccount(const QString &accountId)
{
return new IRCAccount( this, accountId );
}
Kopete::Contact *IRCProtocol::deserializeContact( Kopete::MetaContact *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<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
if( !accounts.isEmpty() )
{
Kopete::Account *a = accounts[ serializedData[ "accountId" ] ];
if( a )
{
a->addContact( contactId, 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, Kopete::ChatSession *manager )
{
IRCAccount *account = static_cast<IRCAccount*>( manager->account() );
if (!args.isEmpty())
{
account->engine()->writeRawMessage(args);
}
else
{
account->appendMessage(i18n("You must enter some text to send to the server."),
IRCAccount::ErrorReply );
}
}
void IRCProtocol::slotQuoteCommand( const QString &args, Kopete::ChatSession *manager )
{
IRCAccount *account = static_cast<IRCAccount*>( manager->account() );
if( !args.isEmpty() )
{
account->engine()->writeMessage( args );
}
else
{
account->appendMessage(i18n("You must enter some text to send to the server."),
IRCAccount::ErrorReply );
}
}
void IRCProtocol::slotCtcpCommand( const QString &args, Kopete::ChatSession *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, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
static_cast<IRCAccount*>( manager->account() )->engine()->motd(argsList.front());
}
void IRCProtocol::slotPingCommand( const QString &args, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments(args);
static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_ping(argsList.front());
}
void IRCProtocol::slotListCommand( const QString &/*args*/, Kopete::ChatSession *manager )
{
static_cast<IRCAccount*>( manager->account() )->listChannels();
}
void IRCProtocol::slotTopicCommand( const QString &args, Kopete::ChatSession *manager )
{
Kopete::ContactPtrList 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()));
}
}
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 &arg, Kopete::ChatSession *manager )
{
QStringList args = Kopete::CommandHandler::parseArguments( arg );
if( KIRC::Entity::isChannel(args[0]) )
{
IRCChannelContact *chan = static_cast<IRCAccount*>( manager->account() )->contactManager()->findChannel( args[0] );
if( args.count() == 2 )
chan->setPassword( args[1] );
static_cast<IRCAccount*>( manager->account() )->engine()->join(args[0], chan->password());
}
else
{
static_cast<IRCAccount*>( manager->account() )->appendMessage(
i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.")
.arg(args[0]), IRCAccount::ErrorReply );
}
}
void IRCProtocol::slotInviteCommand( const QString &args, Kopete::ChatSession *manager )
{
IRCChannelContact *c = 0L;
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
if( argsList.count() > 1 )
{
if( KIRC::Entity::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
{
Kopete::ContactPtrList members = manager->members();
c = dynamic_cast<IRCChannelContact*>( members.first() );
}
if( c && c->manager()->contactOnlineStatus( manager->myself() ) == 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, Kopete::ChatSession *manager )
{
QString user = args.section( ' ', 0, 0 );
QString rest = args.section( ' ', 1 );
if( !KIRC::Entity::isChannel(user) )
{
IRCUserContact *c = static_cast<IRCAccount*>( manager->account() )->
contactManager()->findUser( user );
c->startChat();
if( !rest.isEmpty() )
{
Kopete::Message msg( c->manager()->myself(), c->manager()->members(), rest,
Kopete::Message::Outbound, Kopete::Message::PlainText, CHAT_VIEW);
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, Kopete::ChatSession *manager )
{
static_cast<IRCAccount*>( manager->account() )->engine()->whois( args );
static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
}
void IRCProtocol::slotWhoCommand( const QString &args, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
QString::fromLatin1("WHO %1").arg( argsList.first() ) );
static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
}
void IRCProtocol::slotWhoWasCommand( const QString &args, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
QString::fromLatin1("WHOWAS %1").arg( argsList.first() ) );
static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
}
void IRCProtocol::slotQuitCommand( const QString &args, Kopete::ChatSession *manager )
{
static_cast<IRCAccount*>( manager->account() )->quit( args );
}
void IRCProtocol::slotNickCommand( const QString &args, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
static_cast<IRCAccount*>( manager->account() )->engine()->nick( argsList.front() );
}
void IRCProtocol::slotModeCommand(const QString &args, Kopete::ChatSession *manager)
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
static_cast<IRCAccount*>( manager->account() )->engine()->mode( argsList.front(),
args.section( QRegExp(QString::fromLatin1("\\s+")), 1 ) );
}
void IRCProtocol::slotMeCommand(const QString &args, Kopete::ChatSession *manager)
{
Kopete::ContactPtrList members = manager->members();
static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_action(
static_cast<const IRCContact*>(members.first())->nickName(), args
);
}
void IRCProtocol::slotAllMeCommand(const QString &args, Kopete::ChatSession *)
{
QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
for( QValueList<Kopete::ChatSession*>::iterator it = sessions.begin(); it != sessions.end(); ++it )
{
Kopete::ChatSession *session = *it;
if( session->protocol() == this )
slotMeCommand(args, session);
}
}
void IRCProtocol::slotKickCommand(const QString &args, Kopete::ChatSession *manager)
{
if (manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp)
{
QRegExp spaces(QString::fromLatin1("\\s+"));
QString nick = args.section( spaces, 0, 0);
QString reason = args.section( spaces, 1);
Kopete::ContactPtrList members = manager->members();
QString channel = static_cast<IRCContact*>( members.first() )->nickName();
if (KIRC::Entity::isChannel(channel))
static_cast<IRCAccount*>(manager->account())->engine()->kick(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, Kopete::ChatSession *manager )
{
if( manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
Kopete::ContactPtrList 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, Kopete::ChatSession *manager )
{
QStringList argsList = Kopete::CommandHandler::parseArguments(args);
Kopete::ContactPtrList members = manager->members();
IRCChannelContact *chan = static_cast<IRCChannelContact*>(members.first());
if (chan)
{
if(!args.isEmpty())
static_cast<IRCAccount*>(manager->account())->engine()->part(chan->nickName(), args);
else
chan->part();
if( manager->view() )
manager->view()->closeView(true);
}
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, Kopete::ChatSession *manager )
{
simpleModeChange( args, manager, QString::fromLatin1("+o") );
}
void IRCProtocol::slotDeopCommand( const QString &args, Kopete::ChatSession *manager )
{
simpleModeChange( args, manager, QString::fromLatin1("-o") );
}
void IRCProtocol::slotVoiceCommand( const QString &args, Kopete::ChatSession *manager )
{
simpleModeChange( args, manager, QString::fromLatin1("+v") );
}
void IRCProtocol::slotDevoiceCommand( const QString &args, Kopete::ChatSession *manager )
{
simpleModeChange( args, manager, QString::fromLatin1("-v") );
}
void IRCProtocol::simpleModeChange( const QString &args, Kopete::ChatSession *manager, const QString &mode )
{
if( manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp )
{
QStringList argsList = Kopete::CommandHandler::parseArguments( args );
Kopete::ContactPtrList 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->setIconSet( SmallIconSet( "up" ) );
netConf->downButton->setIconSet( SmallIconSet( "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 ) ) );
connect( netConf->networkList, SIGNAL( doubleClicked ( QListBoxItem * )), SLOT(slotRenameNetwork()));
}
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( 14120 ) << 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 );
disconnect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
netConf->port->setValue( host->port );
connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
netConf->useSSL->setChecked( host->ssl );
netConf->upButton->setEnabled( netConf->hostList->currentItem() > 0 );
netConf->downButton->setEnabled( netConf->hostList->currentItem() < (int)( netConf->hostList->count() - 1 ) );
}
}
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 )
{
disconnect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
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() );
connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
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 = KInputDialog::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 = KInputDialog::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" ) );
if (xmlFile.open(IO_WriteOnly))
{
QTextStream stream(&xmlFile);
stream << doc.toString(4);
xmlFile.close();
}
else
kdDebug(14121) << k_funcinfo << "Failed to save the Networks definition file" << endl;
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 );
QString entryText = selectedHost->host + QString::fromLatin1(":") + QString::number( selectedHost->port );
netConf->hostList->insertItem( entryText, --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 = selectedNetwork->hosts.remove( pos );
selectedNetwork->hosts.insert( ++nextPos, selectedHost );
}
unsigned int currentPos = netConf->hostList->currentItem();
if( currentPos < ( netConf->hostList->count() - 1 ) )
{
netConf->hostList->removeItem( currentPos );
QString entryText = selectedHost->host + QString::fromLatin1(":") + QString::number( selectedHost->port );
netConf->hostList->insertItem( entryText, ++currentPos );
netConf->hostList->setSelected( currentPos, true );
}
}
#include "ircprotocol.moc"
// vim: set noet ts=4 sts=4 sw=4: