diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/irc | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/irc')
74 files changed, 16066 insertions, 0 deletions
diff --git a/kopete/protocols/irc/Makefile.am b/kopete/protocols/irc/Makefile.am new file mode 100644 index 00000000..7bd37813 --- /dev/null +++ b/kopete/protocols/irc/Makefile.am @@ -0,0 +1,40 @@ +METASOURCES = AUTO + +SUBDIRS = icons libkirc ui +AM_CPPFLAGS = -I$(srcdir)/ui $(KOPETE_INCLUDES) \ + -I./ui \ + -I$(srcdir)/libkirc \ + $(all_includes) +kde_module_LTLIBRARIES = kopete_irc.la + +kopete_irc_la_SOURCES = \ + ircaccount.cpp \ + ircaddcontactpage.cpp \ + ircchannelcontact.cpp \ + irccontact.cpp \ + ircguiclient.cpp \ + ircprotocol.cpp \ + ircservercontact.cpp \ + ircsignalhandler.cpp \ + irctransferhandler.cpp \ + ircusercontact.cpp \ + irccontactmanager.cpp \ + kcodecaction.cpp \ + ksparser.cpp + +kopete_irc_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) +kopete_irc_la_LIBADD = ../../libkopete/libkopete.la \ + ./ui/libkopeteircui.la \ + ./libkirc/libkirc.la \ + $(LIB_KIO) + +service_DATA = kopete_irc.desktop irc.protocol +servicedir = $(kde_servicesdir) + +xmldata_DATA = ircnetworks.xml +xmldatadir = $(kde_datadir)/kopete + +EXTRA_DIST = $(xmldata_DATA) + +mydatadir = $(kde_datadir)/kopete +mydata_DATA = ircchatui.rc diff --git a/kopete/protocols/irc/icons/Makefile.am b/kopete/protocols/irc/icons/Makefile.am new file mode 100644 index 00000000..9143c6b4 --- /dev/null +++ b/kopete/protocols/irc/icons/Makefile.am @@ -0,0 +1,2 @@ +kopeteicondir = $(kde_datadir)/kopete/icons +kopeteicon_ICON = AUTO diff --git a/kopete/protocols/irc/icons/cr16-action-irc_away.png b/kopete/protocols/irc/icons/cr16-action-irc_away.png Binary files differnew file mode 100644 index 00000000..d7572e1f --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_away.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_channel.png b/kopete/protocols/irc/icons/cr16-action-irc_channel.png Binary files differnew file mode 100644 index 00000000..0353e7dc --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_channel.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng b/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng Binary files differnew file mode 100644 index 00000000..486102e4 --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng diff --git a/kopete/protocols/irc/icons/cr16-action-irc_normal.png b/kopete/protocols/irc/icons/cr16-action-irc_normal.png Binary files differnew file mode 100644 index 00000000..b7a3cc24 --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_normal.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_online.png b/kopete/protocols/irc/icons/cr16-action-irc_online.png Binary files differnew file mode 100644 index 00000000..c5678d1e --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_online.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_op.png b/kopete/protocols/irc/icons/cr16-action-irc_op.png Binary files differnew file mode 100644 index 00000000..6b14cf14 --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_op.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_server.png b/kopete/protocols/irc/icons/cr16-action-irc_server.png Binary files differnew file mode 100644 index 00000000..b7a3cc24 --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_server.png diff --git a/kopete/protocols/irc/icons/cr16-action-irc_voice.png b/kopete/protocols/irc/icons/cr16-action-irc_voice.png Binary files differnew file mode 100644 index 00000000..6a9b5aaf --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-action-irc_voice.png diff --git a/kopete/protocols/irc/icons/cr16-app-irc_protocol.png b/kopete/protocols/irc/icons/cr16-app-irc_protocol.png Binary files differnew file mode 100644 index 00000000..c5678d1e --- /dev/null +++ b/kopete/protocols/irc/icons/cr16-app-irc_protocol.png diff --git a/kopete/protocols/irc/icons/cr32-app-irc_protocol.png b/kopete/protocols/irc/icons/cr32-app-irc_protocol.png Binary files differnew file mode 100644 index 00000000..f2747b49 --- /dev/null +++ b/kopete/protocols/irc/icons/cr32-app-irc_protocol.png diff --git a/kopete/protocols/irc/irc.protocol b/kopete/protocols/irc/irc.protocol new file mode 100644 index 00000000..f57982bb --- /dev/null +++ b/kopete/protocols/irc/irc.protocol @@ -0,0 +1,12 @@ +[Protocol] +exec=kopete %u +protocol=irc +input=none +output=none +helper=true +listing= +reading=false +writing=false +makedir=false +deleting=false +Icon=irc_normal diff --git a/kopete/protocols/irc/ircaccount.cpp b/kopete/protocols/irc/ircaccount.cpp new file mode 100644 index 00000000..1a1bf75f --- /dev/null +++ b/kopete/protocols/irc/ircaccount.cpp @@ -0,0 +1,904 @@ +/* + ircaccount.cpp - IRC Account + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003-2004 by Jason Keirstead <jason@keirstead.org> + Copyright (c) 2003-2005 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2002-2005 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 "irccontact.h" +#include "irccontactmanager.h" +#include "ircprotocol.h" + +#include "ircservercontact.h" +#include "ircchannelcontact.h" +#include "ircusercontact.h" + +#include "channellistdialog.h" + +#include "kircengine.h" + +#include "kopeteaccountmanager.h" +#include "kopeteaway.h" +#include "kopeteawayaction.h" +#include "kopetecommandhandler.h" +#include "kopetecontactlist.h" +#include "kopetemetacontact.h" +#include "kopeteuiglobal.h" +#include "kopeteview.h" +#include "kopetepassword.h" + +#include <kaction.h> +#include <kaboutdata.h> +#include <kapplication.h> +#include <kconfig.h> +#include <kcompletionbox.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kinputdialog.h> +#include <klineedit.h> +#include <klineeditdlg.h> +#include <kmessagebox.h> +#include <knotifyclient.h> +#include <kpopupmenu.h> + +#include <qlayout.h> +#include <qtimer.h> + +const QString IRCAccount::CONFIG_CODECMIB = QString::fromLatin1("Codec"); +const QString IRCAccount::CONFIG_NETWORKNAME = QString::fromLatin1("NetworkName"); +const QString IRCAccount::CONFIG_NICKNAME = QString::fromLatin1("NickName"); +const QString IRCAccount::CONFIG_USERNAME = QString::fromLatin1("UserName"); +const QString IRCAccount::CONFIG_REALNAME = QString::fromLatin1("RealName"); + +IRCAccount::IRCAccount(IRCProtocol *protocol, const QString &accountId, const QString &autoChan, const QString& netName, const QString &nickName) + : Kopete::PasswordedAccount(protocol, accountId, 0, true), autoConnect( autoChan ), commandSource(0) +{ + m_manager = 0L; + m_channelList = 0L; + m_network = 0L; + + triedAltNick = false; + + m_contactManager = 0; + m_engine = new KIRC::Engine(this); + + QMap< QString, QString> replies = customCtcpReplies(); + for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it ) + m_engine->addCustomCtcp( it.key(), it.data() ); + + QString version=i18n("Kopete IRC Plugin %1 [http://kopete.kde.org]").arg(kapp->aboutData()->version()); + m_engine->setVersionString( version ); + + QObject::connect(m_engine, SIGNAL(successfullyChangedNick(const QString &, const QString &)), + this, SLOT(successfullyChangedNick(const QString &, const QString &))); + + QObject::connect(m_engine, SIGNAL(incomingFailedServerPassword()), + this, SLOT(slotFailedServerPassword())); + + QObject::connect(m_engine, SIGNAL(incomingNickInUse(const QString &)), + this, SLOT(slotNickInUseAlert( const QString &)) ); + + QObject::connect(m_engine, SIGNAL(incomingFailedNickOnLogin(const QString &)), + this, SLOT(slotNickInUse( const QString &)) ); + + QObject::connect(m_engine, SIGNAL(incomingJoinedChannel(const QString &, const QString &)), + this, SLOT(slotJoinedUnknownChannel(const QString &, const QString &))); + + QObject::connect(m_engine, SIGNAL(incomingCtcpReply(const QString &, const QString &, const QString &)), + this, SLOT( slotNewCtcpReply(const QString&, const QString &, const QString &))); + + QObject::connect(m_engine, SIGNAL(statusChanged(KIRC::Engine::Status)), + this, SLOT(engineStatusChanged(KIRC::Engine::Status))); + + QObject::connect(m_engine, SIGNAL(incomingServerLoadTooHigh()), + this, SLOT(slotServerBusy())); + + QObject::connect(m_engine, SIGNAL(incomingNoSuchNickname(const QString &)), + this, SLOT(slotNoSuchNickname(const QString &))); + + mAwayAction = new Kopete::AwayAction ( i18n("Set Away"), + m_protocol->m_UserStatusAway.iconFor( this ), 0, this, + SLOT(slotGoAway( const QString & )), this ); + + currentHost = 0; + + KConfigGroup *config = configGroup(); + + QString networkName = netName; + if (networkName.isNull()) + networkName = config->readEntry(CONFIG_NETWORKNAME); + + if (!nickName.isNull()) + setNickName(nickName); + else + mNickName = config->readEntry(CONFIG_NICKNAME); + + QString codecMib = config->readEntry(CONFIG_CODECMIB); + // int codecMib = config->readNumEntry(CONFIG_CODECMIB, UTF-8); + + m_serverNotices = (MessageDestination)config->readNumEntry( "ServerNotices", ServerWindow ); + m_serverMessages = (MessageDestination)config->readNumEntry( "ServerMessages", ServerWindow ); + m_informationReplies = (MessageDestination)config->readNumEntry( "InformationReplies", ActiveWindow ); + m_errorMessages = (MessageDestination)config->readNumEntry( "ErrorMessages", ActiveWindow ); + autoShowServerWindow = config->readBoolEntry( "AutoShowServerWindow", false ); + + if( !codecMib.isEmpty() ) + { + mCodec = QTextCodec::codecForMib( codecMib.toInt() ); + m_engine->setDefaultCodec( mCodec ); + } + else + mCodec = 0; + + QString m_accountId = this->accountId(); + if( networkName.isEmpty() && QRegExp( "[^#+&\\s]+@[\\w-\\.]+:\\d+" ).exactMatch( m_accountId ) ) + { + kdDebug(14120) << "Creating account from " << m_accountId << endl; + + mNickName = m_accountId.section('@',0,0); + QString serverInfo = m_accountId.section('@',1); + QString hostName = serverInfo.section(':',0,0); + + for( QDictIterator<IRCNetwork> it( m_protocol->networks() ); it.current(); ++it ) + { + IRCNetwork *net = it.current(); + for( QValueList<IRCHost*>::iterator it2 = net->hosts.begin(); it2 != net->hosts.end(); ++it2 ) + { + if( (*it2)->host == hostName ) + { + setNetwork(net->name); + break; + } + } + + if( !networkName.isEmpty() ) + break; + } + + if( networkName.isEmpty() ) + { + /* Could not find this host. Add it to the networks structure */ + + m_network = new IRCNetwork; + m_network->name = i18n("Temporary Network - %1").arg( hostName ); + m_network->description = i18n("Network imported from previous version of Kopete, or an IRC URI"); + + IRCHost *host = new IRCHost; + host->host = hostName; + host->port = serverInfo.section(':',1).toInt(); + if( !password().cachedValue().isEmpty() ) + host->password = password().cachedValue(); + host->ssl = false; + + m_network->hosts.append( host ); + m_protocol->addNetwork( m_network ); + + config->writeEntry(CONFIG_NETWORKNAME, m_network->name); + config->writeEntry(CONFIG_NICKNAME, mNickName); + } + } + else if( !networkName.isEmpty() ) + { + setNetwork(networkName); + } + else + { + kdError() << "No network name defined, and could not import network information from ID" << endl; + } + + m_engine->setUserName(userName()); + m_engine->setRealName(realName()); + + m_contactManager = new IRCContactManager(mNickName, this); + setMyself( m_contactManager->mySelf() ); + setAccountLabel( QString::fromLatin1("%1@%2").arg(mNickName,networkName) ); + m_myServer = m_contactManager->myServer(); + + m_joinChannelAction = new KAction ( i18n("Join Channel..."), QString::null, 0, this, + SLOT(slotJoinChannel()), this); + m_searchChannelAction = new KAction ( i18n("Search Channels..."), QString::null, 0, this, + SLOT(slotSearchChannels()), this); +} + +IRCAccount::~IRCAccount() +{ + if (m_engine->isConnected()) + m_engine->quit(i18n("Plugin Unloaded"), true); +} + +void IRCAccount::slotNickInUse( const QString &nick ) +{ + QString altNickName = altNick(); + if( triedAltNick || altNickName.isEmpty() ) + { + QString newNick = KInputDialog::getText( + i18n("IRC Plugin"), + i18n("The nickname %1 is already in use. Please enter an alternate nickname:").arg(nick), + nick); + + if (newNick.isNull()) + disconnect(); + else + m_engine->nick(newNick); + } + else + { + triedAltNick = true; + m_engine->nick(altNickName); + } +} + +void IRCAccount::slotNickInUseAlert( const QString &nick ) +{ + KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("The nickname %1 is already in use").arg(nick), i18n("IRC Plugin")); +} + +void IRCAccount::setAltNick( const QString &altNick ) +{ + configGroup()->writeEntry(QString::fromLatin1( "altNick" ), altNick); +} + +const QString IRCAccount::altNick() const +{ + return configGroup()->readEntry(QString::fromLatin1("altNick")); +} + +void IRCAccount::setAutoShowServerWindow( bool show ) +{ + autoShowServerWindow = show; + configGroup()->writeEntry(QString::fromLatin1( "AutoShowServerWindow" ), autoShowServerWindow); +} + +const QString IRCAccount::networkName() const +{ + if( m_network ) + return m_network->name; + else + return i18n("Unknown"); +} + +void IRCAccount::setUserName( const QString &userName ) +{ + m_engine->setUserName(userName); + configGroup()->writeEntry(CONFIG_USERNAME, userName); +} + +const QString IRCAccount::userName() const +{ + return configGroup()->readEntry(CONFIG_USERNAME); +} + +void IRCAccount::setRealName( const QString &userName ) +{ + m_engine->setRealName(userName); + configGroup()->writeEntry(CONFIG_REALNAME, userName); +} + +const QString IRCAccount::realName() const +{ + return configGroup()->readEntry(CONFIG_REALNAME); +} + +void IRCAccount::setNetwork( const QString &network ) +{ + IRCNetwork *net = m_protocol->networks()[ network ]; + if( net ) + { + m_network = net; + configGroup()->writeEntry(CONFIG_NETWORKNAME, network); + setAccountLabel(network); + } + else + { + KMessageBox::queuedMessageBox( + Kopete::UI::Global::mainWidget(), KMessageBox::Error, + i18n("<qt>The network associated with this account, <b>%1</b>, no longer exists. Please" + " ensure that the account has a valid network. The account will not be enabled until you do so.</qt>").arg(network), + i18n("Problem Loading %1").arg( accountId() ), 0 ); + } +} + +void IRCAccount::setNickName( const QString &nick ) +{ + mNickName = nick; + configGroup()->writeEntry(CONFIG_NICKNAME, mNickName); + + if( mySelf() ) + mySelf()->setNickName( mNickName ); +} + +// FIXME: Possible null pointer usage here +void IRCAccount::setCodec( QTextCodec *codec ) +{ + mCodec = codec; + configGroup()->writeEntry(CONFIG_CODECMIB, codec->mibEnum()); + + if( mCodec ) + m_engine->setDefaultCodec( mCodec ); +} + +QTextCodec *IRCAccount::codec() const +{ + return mCodec; +} + +// FIXME: Move this to a dictionnary +void IRCAccount::setDefaultPart( const QString &defaultPart ) +{ + configGroup()->writeEntry( QString::fromLatin1( "defaultPart" ), defaultPart ); +} + +// FIXME: Move this to a dictionnary +void IRCAccount::setDefaultQuit( const QString &defaultQuit ) +{ + configGroup()->writeEntry( QString::fromLatin1( "defaultQuit" ), defaultQuit ); +} + +// FIXME: Move this to a dictionnary +const QString IRCAccount::defaultPart() const +{ + QString partMsg = configGroup()->readEntry(QString::fromLatin1("defaultPart")); + if( partMsg.isEmpty() ) + return QString::fromLatin1("Kopete %1 : http://kopete.kde.org").arg( kapp->aboutData()->version() ); + return partMsg; +} + +const QString IRCAccount::defaultQuit() const +{ + QString quitMsg = configGroup()->readEntry(QString::fromLatin1("defaultQuit")); + if( quitMsg.isEmpty() ) + return QString::fromLatin1("Kopete %1 : http://kopete.kde.org").arg(kapp->aboutData()->version()); + return quitMsg; +} + +void IRCAccount::setCustomCtcpReplies( const QMap< QString, QString > &replies ) const +{ + QStringList val; + for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it ) + { + m_engine->addCustomCtcp( it.key(), it.data() ); + val.append( QString::fromLatin1("%1=%2").arg( it.key() ).arg( it.data() ) ); + } + + configGroup()->writeEntry( "CustomCtcp", val ); +} + +const QMap< QString, QString > IRCAccount::customCtcpReplies() const +{ + QMap< QString, QString > replies; + QStringList replyList; + + replyList = configGroup()->readListEntry( "CustomCtcp" ); + + for( QStringList::Iterator it = replyList.begin(); it != replyList.end(); ++it ) + replies[ (*it).section('=', 0, 0 ) ] = (*it).section('=', 1 ); + + return replies; +} + +void IRCAccount::setConnectCommands( const QStringList &commands ) const +{ + configGroup()->writeEntry( "ConnectCommands", commands ); +} + +const QStringList IRCAccount::connectCommands() const +{ + return configGroup()->readListEntry( "ConnectCommands" ); +} + +void IRCAccount::setMessageDestinations( int serverNotices, int serverMessages, + int informationReplies, int errorMessages ) +{ + KConfigGroup *config = configGroup(); + config->writeEntry( "ServerNotices", serverNotices ); + config->writeEntry( "ServerMessages", serverMessages ); + config->writeEntry( "InformationReplies", informationReplies ); + config->writeEntry( "ErrorMessages", errorMessages ); + + m_serverNotices = (MessageDestination)serverNotices; + m_serverMessages = (MessageDestination)serverMessages; + m_informationReplies = (MessageDestination)informationReplies; + m_errorMessages = (MessageDestination)errorMessages; +} + +KActionMenu *IRCAccount::actionMenu() +{ + QString menuTitle = QString::fromLatin1( " %1 <%2> " ).arg( accountId() ).arg( myself()->onlineStatus().description() ); + + KActionMenu *mActionMenu = Kopete::Account::actionMenu(); + + m_joinChannelAction->setEnabled( isConnected() ); + m_searchChannelAction->setEnabled( isConnected() ); + + mActionMenu->popupMenu()->insertSeparator(); + mActionMenu->insert(m_joinChannelAction); + mActionMenu->insert(m_searchChannelAction); + mActionMenu->insert( new KAction ( i18n("Show Server Window"), QString::null, 0, this, SLOT(slotShowServerWindow()), mActionMenu ) ); + + if( m_engine->isConnected() && m_engine->useSSL() ) + { + mActionMenu->insert( new KAction ( i18n("Show Security Information"), "", 0, m_engine, + SLOT(showInfoDialog()), mActionMenu ) ); + } + + return mActionMenu; +} + +void IRCAccount::connectWithPassword(const QString &password) +{ + //TODO: honor the initial status + + if( m_engine->isConnected() ) + { + if( isAway() ) + setAway( false ); + } + else if( m_engine->isDisconnected() ) + { + if( m_network ) + { + QValueList<IRCHost*> &hosts = m_network->hosts; + if( hosts.count() == 0 ) + { + KMessageBox::queuedMessageBox( + Kopete::UI::Global::mainWidget(), KMessageBox::Error, + i18n("<qt>The network associated with this account, <b>%1</b>, has no valid hosts. Please ensure that the account has a valid network.</qt>").arg(m_network->name), + i18n("Network is Empty"), 0 ); + } + else if( currentHost == hosts.count() ) + { + KMessageBox::queuedMessageBox( + Kopete::UI::Global::mainWidget(), KMessageBox::Error, + i18n("<qt>Kopete could not connect to any of the servers in the network associated with this account (<b>%1</b>). Please try again later.</qt>").arg(m_network->name), + i18n("Network is Unavailable"), 0 ); + + currentHost = 0; + } + else + { + // if prefer SSL is set, sort by SSL first + if (configGroup()->readBoolEntry("PreferSSL")) + { + typedef QValueList<IRCHost*> IRCHostList; + IRCHostList sslFirst; + IRCHostList::iterator it; + for ( it = hosts.begin(); it != hosts.end(); ++it ) + { + if ( (*it)->ssl == true ) + { + sslFirst.append( *it ); + it = hosts.remove( it ); + } + } + for ( it = hosts.begin(); it != hosts.end(); ++it ) + sslFirst.append( *it ); + + hosts = sslFirst; + } + + IRCHost *host = hosts[ currentHost++ ]; + myServer()->appendMessage( i18n("Connecting to %1...").arg( host->host ) ); + if( host->ssl ) + myServer()->appendMessage( i18n("Using SSL") ); + + m_engine->setPassword(password); + m_engine->connectToServer( host->host, host->port, mNickName, host->ssl ); + } + } + else + { + kdWarning() << "No network defined!" << endl; + } + } +} + +void IRCAccount::engineStatusChanged(KIRC::Engine::Status newStatus) +{ + kdDebug(14120) << k_funcinfo << endl; + + mySelf()->updateStatus(); + + switch (newStatus) + { + case KIRC::Engine::Idle: + // Do nothing. + break; + case KIRC::Engine::Connecting: + { + if( autoShowServerWindow ) + myServer()->startChat(); + break; + } + case KIRC::Engine::Authentifying: + break; + case KIRC::Engine::Connected: + { + //Reset the host so re-connection will start over at first server + currentHost = 0; + m_contactManager->addToNotifyList( m_engine->nickName() ); + + // HACK! See bug #85200 for details. Some servers still cannot accept commands + // after the 001 is sent, you need to wait until all the init junk is done. + // Unfortunatly, there is no way for us to know when it is done (it could be + // spewing out any number of replies), so just try delaying it + QTimer::singleShot( 250, this, SLOT( slotPerformOnConnectCommands() ) ); + } + break; + case KIRC::Engine::Closing: + triedAltNick = false; +// mySelf()->setOnlineStatus( m_protocol->m_UserStatusOffline ); + m_contactManager->removeFromNotifyList( m_engine->nickName() ); + +// if (m_contactManager && !autoConnect.isNull()) +// Kopete::AccountManager::self()->removeAccount( this ); + break; + case KIRC::Engine::AuthentifyingFailed: + break; + case KIRC::Engine::Timeout: + //Try next server + connect(); + break; + case KIRC::Engine::Disconnected: + break; + } +} + +void IRCAccount::slotPerformOnConnectCommands() +{ + Kopete::ChatSession *manager = myServer()->manager(Kopete::Contact::CanCreate); + if (!manager) + return; + + if (!autoConnect.isEmpty()) + Kopete::CommandHandler::commandHandler()->processMessage( QString::fromLatin1("/join %1").arg(autoConnect), manager); + + QStringList commands(connectCommands()); + for (QStringList::Iterator it=commands.begin(); it != commands.end(); ++it) + Kopete::CommandHandler::commandHandler()->processMessage(*it, manager); +} + +void IRCAccount::slotJoinedUnknownChannel(const QString &channel, const QString &nick) +{ + if ( nick.lower() == m_contactManager->mySelf()->nickName().lower() ) + { + m_contactManager->findChannel(channel)->join(); + } +} + +void IRCAccount::disconnect() +{ + quit(); +} + +void IRCAccount::slotServerBusy() +{ + KMessageBox::queuedMessageBox( + Kopete::UI::Global::mainWidget(), KMessageBox::Error, + i18n("The IRC server is currently too busy to respond to this request."), + i18n("Server is Busy"), 0 + ); +} + +void IRCAccount::slotSearchChannels() +{ + if( !m_channelList ) + { + m_channelList = new ChannelListDialog( m_engine, + i18n("Channel List for %1").arg( m_engine->currentHost() ), this, + SLOT( slotJoinNamedChannel( const QString & ) ) ); + } + else + m_channelList->clear(); + + m_channelList->show(); +} + +void IRCAccount::listChannels() +{ + slotSearchChannels(); + m_channelList->search(); +} + +void IRCAccount::quit( const QString &quitMessage ) +{ + kdDebug(14120) << "Quitting IRC: " << quitMessage << endl; + + if( quitMessage.isNull() || quitMessage.isEmpty() ) + m_engine->quit( defaultQuit() ); + else + m_engine->quit( quitMessage ); +} + +void IRCAccount::setAway(bool isAway, const QString &awayMessage) +{ + kdDebug(14120) << k_funcinfo << isAway << " " << awayMessage << endl; + if(m_engine->isConnected()) + { + static_cast<IRCUserContact *>( myself() )->setAway( isAway ); + engine()->away(isAway, awayMessage); + } +} + +/* + * Ask for server password, and reconnect + */ +void IRCAccount::slotFailedServerPassword() +{ + // JLN + password().setWrong(); + connect(); +} +void IRCAccount::slotGoAway( const QString &reason ) +{ + setAway( true, reason ); +} + +void IRCAccount::slotShowServerWindow() +{ + m_myServer->startChat(); +} + +bool IRCAccount::isConnected() +{ +// return ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline ); + return m_engine->isConnected(); +} + +void IRCAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason ) +{ + if ( status.status() == Kopete::OnlineStatus::Online && + myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline ) + connect(); + else if (status.status() == Kopete::OnlineStatus::Online && + myself()->onlineStatus().status() == Kopete::OnlineStatus::Away ) + setAway( false ); + else if ( status.status() == Kopete::OnlineStatus::Offline ) + disconnect(); + else if ( status.status() == Kopete::OnlineStatus::Away ) + slotGoAway( reason ); +} + + +void IRCAccount::successfullyChangedNick(const QString &oldnick, const QString &newnick) +{ + kdDebug(14120) << k_funcinfo << "Changing nick to " << newnick << endl; + mNickName = newnick; + mySelf()->setNickName( mNickName ); + m_contactManager->removeFromNotifyList( oldnick ); + m_contactManager->addToNotifyList( newnick ); +} + +bool IRCAccount::createContact( const QString &contactId, Kopete::MetaContact *m ) +{ + kdDebug(14120) << k_funcinfo << contactManager() << endl; + IRCContact *c; + + if( !m ) + {//This should NEVER happen + m = new Kopete::MetaContact(); + Kopete::ContactList::self()->addMetaContact(m); + } + + if( contactId == mNickName ) + { + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n("\"You are not allowed to add yourself to your contact list."), i18n("IRC Plugin") + ); + + return false; + } + else if (contactId.startsWith(QString::fromLatin1("#"))) + { + c = static_cast<IRCContact*>(contactManager()->findChannel(contactId, m)); + } + else + { + m_contactManager->addToNotifyList( contactId ); + c = static_cast<IRCContact*>(contactManager()->findUser(contactId, m)); + } + + if( c->metaContact() != m ) + {//This should NEVER happen + Kopete::MetaContact *old = c->metaContact(); + c->setMetaContact( m ); + Kopete::ContactPtrList children = old->contacts(); + if (children.isEmpty()) + Kopete::ContactList::self()->removeMetaContact( old ); + } + else if( c->metaContact()->isTemporary() ) + m->setTemporary(false); + + return true; +} + +void IRCAccount::slotJoinNamedChannel(const QString &chan) +{ + contactManager()->findChannel(chan)->startChat(); +} + +void IRCAccount::setCurrentCommandSource( Kopete::ChatSession *session ) +{ + commandSource = session; +} + +Kopete::ChatSession *IRCAccount::currentCommandSource() +{ + return commandSource; +} + +void IRCAccount::slotJoinChannel() +{ + if (!isConnected()) + return; + + QStringList chans = configGroup()->readListEntry( "Recent Channel list" ); + //kdDebug(14120) << "Recent channel list from config: " << chans << endl; + + KLineEditDlg dlg( + i18n("Please enter name of the channel you want to join:"), + QString::null, + Kopete::UI::Global::mainWidget() + ); + + KCompletion comp; + comp.insertItems( chans ); + + dlg.lineEdit()->setCompletionObject( &comp ); + dlg.lineEdit()->setCompletionMode( KGlobalSettings::CompletionPopup ); + + while( true ) + { + if( dlg.exec() != QDialog::Accepted ) + break; + + QString chan = dlg.text(); + if( chan.isNull() ) + break; + + if( KIRC::Entity::isChannel( chan ) ) + { + contactManager()->findChannel( chan )->startChat(); + + // push the joined channel to first in list + chans.remove( chan ); + chans.prepend( chan ); + + configGroup()->writeEntry( "Recent Channel list", chans ); + break; + } + + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.").arg(chan), + i18n("IRC Plugin") + ); + } +} + +void IRCAccount::slotNewCtcpReply(const QString &type, const QString &/*target*/, const QString &messageReceived) +{ + appendMessage( i18n("CTCP %1 REPLY: %2").arg(type).arg(messageReceived), InfoReply ); +} + +void IRCAccount::slotNoSuchNickname( const QString &nick ) +{ + if( KIRC::Entity::isChannel(nick) ) + appendMessage( i18n("The channel \"%1\" does not exist").arg(nick), UnknownReply ); + else + appendMessage( i18n("The nickname \"%1\" does not exist").arg(nick), UnknownReply ); +} + +void IRCAccount::appendMessage( const QString &message, MessageType type ) +{ + // TODO: Impliment a UI where people can pick multiple destinations + // for a message type, and make codethis handle it + + MessageDestination destination; + + switch( type ) + { + case ConnectReply: + destination = m_serverMessages; + break; + case InfoReply: + destination = m_informationReplies; + break; + case NoticeReply: + destination = m_serverNotices; + break; + case ErrorReply: + destination = m_errorMessages; + break; + case UnknownReply: + default: + destination = ActiveWindow; + break; + } + + if( destination == ActiveWindow ) + { + KopeteView *activeView = Kopete::ChatSessionManager::self()->activeView(); + if( activeView && activeView->msgManager()->account() == this ) + { + Kopete::ChatSession *manager = activeView->msgManager(); + Kopete::Message msg( manager->myself(), manager->members(), message, + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW ); + activeView->appendMessage(msg); + } + } + + if( destination == AnonymousWindow ) + { + //TODO: Create an anonymous window??? What will this mean... + } + + if( destination == ServerWindow ) + { + myServer()->appendMessage(message); + } + + if( destination == KNotify ) + { + KNotifyClient::event( + Kopete::UI::Global::mainWidget()->winId(), QString::fromLatin1("irc_event"), message + ); + } +} + +IRCUserContact *IRCAccount::mySelf() const +{ + return static_cast<IRCUserContact *>( myself() ); +} + +IRCServerContact *IRCAccount::myServer() const +{ + return m_myServer; +} + +IRCContact *IRCAccount::getContact(const QString &name, Kopete::MetaContact *metac) +{ + return getContact(m_engine->getEntity(name), metac); +} + +IRCContact *IRCAccount::getContact(KIRC::EntityPtr entity, Kopete::MetaContact *metac) +{ + IRCContact *contact = 0; + +#ifdef __GNUC__ + #warning Do the search code here. +#endif + + if (!contact) + { +#ifdef __GNUC__ + #warning Make a temporary meta contact if metac is null +#endif + contact = new IRCContact(this, entity, metac); + m_contacts.append(contact); + } + + QObject::connect(contact, SIGNAL(destroyed(IRCContact *)), SLOT(destroyed(IRCContact *))); + return contact; +} + +void IRCAccount::destroyed(IRCContact *contact) +{ + m_contacts.remove(contact); +} + +#include "ircaccount.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ircaccount.h b/kopete/protocols/irc/ircaccount.h new file mode 100644 index 00000000..e5917360 --- /dev/null +++ b/kopete/protocols/irc/ircaccount.h @@ -0,0 +1,248 @@ +/* + ircaccount.h - IRC Account + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCACCOUNT_H +#define IRCACCOUNT_H + +#include "ircprotocol.h" + +#include "kircengine.h" + +#include "kopetepasswordedaccount.h" + +#include <kdialogbase.h> + +#include <qstring.h> +#include <qstringlist.h> + +class ChannelListDialog; + +class IRCContact; +class IRCChannelContact; +class IRCContactManager; +class IRCServerContact; +class IRCProtocol; +class IRCUserContact; + +namespace Kopete +{ +class AwayAction; +class Contact; +class Message; +class ChatSession; +class MetaContact; +} + +class KAction; +class KActionMenu; + +struct IRCHost +{ + QString host; + uint port; + QString password; + bool ssl; +}; + +struct IRCNetwork +{ + QString name; + QString description; + QValueList<IRCHost*> hosts; +}; + +class IRCAccount + : public Kopete::PasswordedAccount +{ + friend class IRCEditAccountWidget; + friend class IRCProtocolHandler; + + Q_OBJECT + +public: + static const QString CONFIG_CODECMIB; + static const QString CONFIG_NETWORKNAME; + static const QString CONFIG_NICKNAME; + static const QString CONFIG_USERNAME; + static const QString CONFIG_REALNAME; + + enum MessageType + { + ConnectReply = 1, + InfoReply = 2, + NoticeReply = 4, + ErrorReply = 8, + UnknownReply = 16, + Default = 32 + }; + + enum MessageDestination + { + ActiveWindow = 1, + ServerWindow = 2, + AnonymousWindow = 3, + KNotify = 4, + Ignore = 5 + }; + + IRCAccount(IRCProtocol *p, const QString &accountid, const QString &autoConnect = QString::null, + const QString& networkName = QString::null, const QString &nickName = QString::null); + virtual ~IRCAccount(); + + void setNickName( const QString & ); + + void setAutoShowServerWindow( bool show ); + + void setAltNick( const QString & ); + const QString altNick() const; + + void setUserName( const QString & ); + const QString userName() const; + + void setRealName( const QString & ); + const QString realName() const; + + const QStringList connectCommands() const; + + void setConnectCommands( const QStringList & ) const; + + void setDefaultPart( const QString & ); + + void setNetwork( const QString & ); + + void setDefaultQuit( const QString & ); + + void setCodec( QTextCodec *codec ); + + void setMessageDestinations( int serverNotices, int serverMessages, + int informationReplies, int errorMessages ); + + QTextCodec *codec() const; + + const QString defaultPart() const; + + const QString defaultQuit() const; + + const QString networkName() const; + + QMap< QString, QString > customCtcp() const; + + void setCustomCtcpReplies( const QMap< QString, QString > &replys ) const; + + const QMap<QString, QString> customCtcpReplies() const; + + void setCurrentCommandSource( Kopete::ChatSession *session ); + + Kopete::ChatSession *currentCommandSource(); + + IRCContact *getContact(const QString &name, Kopete::MetaContact *metac=0); + IRCContact *getContact(KIRC::EntityPtr entity, Kopete::MetaContact *metac=0); + +public slots: + + virtual KActionMenu *actionMenu(); + + virtual void setAway( bool isAway, const QString &awayMessage = QString::null ); + + virtual bool isConnected(); + + /** Reimplemented from Kopete::Account */ + void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null); + + // Returns the KIRC engine instance + KIRC::Engine *engine() const { return m_engine; } + + // Returns the IRCProtocol instance for contacts + IRCProtocol *protocol() const { return m_protocol; } + + IRCContactManager *contactManager() const { return m_contactManager; } + + // Returns the Kopete::Contact of the user + IRCUserContact *mySelf() const; + + // Returns the Kopete::Contact of the server of the user + IRCServerContact *myServer() const; + + void successfullyChangedNick(const QString &, const QString &); + + virtual void connectWithPassword( const QString & ); + virtual void disconnect(); + + void quit( const QString &quitMessage = QString::null ); + + void listChannels(); + + void appendMessage( const QString &message, MessageType type = Default ); + +protected: + virtual bool createContact( const QString &contactId, Kopete::MetaContact *parentContact ) ; + +private slots: + void engineStatusChanged(KIRC::Engine::Status newStatus); + + void destroyed(IRCContact *contact); + + void slotFailedServerPassword(); + void slotGoAway( const QString &reason ); + void slotJoinNamedChannel( const QString &channel ); + void slotJoinChannel(); + void slotShowServerWindow(); + void slotNickInUse( const QString &nick ); + void slotNickInUseAlert( const QString &nick ); + void slotServerBusy(); + void slotNoSuchNickname( const QString &nick ); + void slotSearchChannels(); + void slotNewCtcpReply(const QString &type, const QString &target, const QString &messageReceived); + void slotJoinedUnknownChannel( const QString &channel, const QString &nick ); + void slotPerformOnConnectCommands(); + +private: + Kopete::ChatSession *m_manager; + QString mNickName; + Kopete::AwayAction *mAwayAction; + bool triedAltNick; + bool autoShowServerWindow; + QString autoConnect; + + KIRC::Engine *m_engine; + IRCNetwork *m_network; + uint currentHost; + QTextCodec *mCodec; + + MessageDestination m_serverNotices; + MessageDestination m_serverMessages; + MessageDestination m_informationReplies; + MessageDestination m_errorMessages; + + ChannelListDialog *m_channelList; + + QValueList<IRCContact *> m_contacts; + IRCContactManager *m_contactManager; + IRCServerContact *m_myServer; + + QMap< QString, QString > m_customCtcp; + Kopete::ChatSession *commandSource; + + KAction *m_joinChannelAction; + KAction *m_searchChannelAction; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ircaddcontactpage.cpp b/kopete/protocols/irc/ircaddcontactpage.cpp new file mode 100644 index 00000000..db4ca3b2 --- /dev/null +++ b/kopete/protocols/irc/ircaddcontactpage.cpp @@ -0,0 +1,83 @@ +/* + ircaddcontactpage.cpp - IRC Add Contact Widget + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002 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 "ircadd.h" +#include "ircaddcontactpage.h" +#include "channellist.h" + +#include "kircengine.h" + +#include "ircaccount.h" + +#include <qlayout.h> +#include <qlineedit.h> +#include <qframe.h> +#include <qtabwidget.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> + +IRCAddContactPage::IRCAddContactPage( QWidget *parent, IRCAccount *a ) : AddContactPage(parent, 0) +{ + (new QVBoxLayout(this))->setAutoAdd(true); + ircdata = new ircAddUI(this); + mSearch = new ChannelList( (QWidget*)ircdata->hbox, a->engine() ); + mAccount = a; + + connect( mSearch, SIGNAL( channelSelected( const QString & ) ), + this, SLOT( slotChannelSelected( const QString & ) ) ); + + connect( mSearch, SIGNAL( channelDoubleClicked( const QString & ) ), + this, SLOT( slotChannelDoubleClicked( const QString & ) ) ); +} + +IRCAddContactPage::~IRCAddContactPage() +{ +} + +void IRCAddContactPage::slotChannelSelected( const QString &channel ) +{ + ircdata->addID->setText( channel ); +} + +void IRCAddContactPage::slotChannelDoubleClicked( const QString &channel ) +{ + ircdata->addID->setText( channel ); + ircdata->tabWidget3->setCurrentPage(0); +} + +bool IRCAddContactPage::apply(Kopete::Account *account , Kopete::MetaContact *m) +{ + QString name = ircdata->addID->text(); + return account->addContact(name, m, Kopete::Account::ChangeKABC ); +} + +bool IRCAddContactPage::validateData() +{ + QString name = ircdata->addID->text(); + if (name.isEmpty() == true) + { + KMessageBox::sorry(this, i18n("<qt>You need to specify a channel to join, or query to open.</qt>"), i18n("You Must Specify a Channel")); + return false; + } + return true; +} + +#include "ircaddcontactpage.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ircaddcontactpage.h b/kopete/protocols/irc/ircaddcontactpage.h new file mode 100644 index 00000000..c6b897ff --- /dev/null +++ b/kopete/protocols/irc/ircaddcontactpage.h @@ -0,0 +1,61 @@ +/* + ircaddcontactpage.h - IRC Add Contact Widget + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCADDCONTACTPAGE_H +#define IRCADDCONTACTPAGE_H + +#include "addcontactpage.h" + +class ircAddUI; +namespace Kopete { class MetaContact; } +class IRCAccount; +class QListViewItem; +class ChannelList; + +/** + *@author Nick Betcher <nbetcher@kde.org> + */ +class IRCAddContactPage : public AddContactPage +{ + Q_OBJECT +public: + IRCAddContactPage(QWidget *parent=0, IRCAccount* account = 0); + ~IRCAddContactPage(); + ircAddUI *ircdata; + +public slots: + virtual bool apply(Kopete::Account *account , Kopete::MetaContact *m); + +private slots: + virtual bool validateData(); + void slotChannelSelected( const QString &channel ); + void slotChannelDoubleClicked( const QString &channel ); +private: + IRCAccount *mAccount; + ChannelList *mSearch; +}; + +#endif +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ircchannelcontact.cpp b/kopete/protocols/irc/ircchannelcontact.cpp new file mode 100644 index 00000000..cc99acf3 --- /dev/null +++ b/kopete/protocols/irc/ircchannelcontact.cpp @@ -0,0 +1,749 @@ +/* + ircchannelcontact.cpp - IRC Channel Contact + + 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 "irccontactmanager.h" +#include "ircchannelcontact.h" +#include "ircusercontact.h" +#include "ircservercontact.h" +#include "ircaccount.h" +#include "ircprotocol.h" + +#include "kopeteview.h" +#include "kopeteuiglobal.h" +#include "kcodecaction.h" +#include "kopetemetacontact.h" +#include "kopetestdaction.h" +#include "kopetechatsessionmanager.h" + +#include <kdebug.h> +#include <krun.h> +#include <kinputdialog.h> +#include <kapplication.h> +#include <kaboutdata.h> +#include <kglobal.h> +#include <kmessagebox.h> + +#include <qtimer.h> + +//This is the number of nicknames we will process concurrently when joining a channel +//Lower numbers ensure less GUI blocking, but take marginally longer to complete. +//Higher numbers are absolute fastest, but block GUI until all members are added +#define NICK_BATCH_LENGTH 1 + +IRCChannelContact::IRCChannelContact(IRCContactManager *contactManager, const QString &channel, Kopete::MetaContact *metac) + : IRCContact(contactManager, channel, metac, "irc_channel") +{ + KIRC::Engine *engine = kircEngine(); + + mInfoTimer = new QTimer( this ); + QObject::connect(mInfoTimer, SIGNAL(timeout()), this, SLOT( slotUpdateInfo() ) ); + + QObject::connect(engine, SIGNAL(incomingUserIsAway(const QString &, const QString &)), + this, SLOT(slotIncomingUserIsAway(const QString &, const QString &))); + + QObject::connect(engine, SIGNAL(incomingListedChan(const QString &, uint, const QString &)), + this, SLOT(slotChannelListed(const QString &, uint, const QString &))); + + actionJoin = 0L; + actionModeT = new KToggleAction(i18n("Only Operators Can Change &Topic"), 0, this, SLOT(slotModeChanged()), this ); + actionModeN = new KToggleAction(i18n("&No Outside Messages"), 0, this, SLOT(slotModeChanged()), this ); + actionModeS = new KToggleAction(i18n("&Secret"), 0, this, SLOT(slotModeChanged()), this ); + actionModeM = new KToggleAction(i18n("&Moderated"), 0, this, SLOT(slotModeChanged()), this ); + actionModeI = new KToggleAction(i18n("&Invite Only"), 0, this, SLOT(slotModeChanged()), this ); + actionHomePage = 0L; + + updateStatus(); +} + +IRCChannelContact::~IRCChannelContact() +{ +} + +void IRCChannelContact::slotUpdateInfo() +{ + /** This woudl be nice, but it generates server errors too often + + if( !manager(Kopete::Contact::CannotCreate) && onlineStatus() == m_protocol->m_ChannelStatusOnline ) + kircEngine()->writeMessage( QString::fromLatin1("LIST %1").arg(m_nickName) ); + else + setProperty( QString::fromLatin1("channelMembers"), i18n("Members"), manager()->members().count() ); + + */ + KIRC::Engine *engine = kircEngine(); + + if (manager(Kopete::Contact::CannotCreate)) + { + setProperty(m_protocol->propChannelMembers, manager()->members().count()); + engine->writeMessage(QString::fromLatin1("WHO %1").arg(m_nickName)); + } + else + { + removeProperty(m_protocol->propChannelMembers); + removeProperty(m_protocol->propChannelTopic); + } + + mInfoTimer->start( 45000, true ); +} + +void IRCChannelContact::slotChannelListed( const QString &channel, uint members, const QString &topic ) +{ + if (!manager(Kopete::Contact::CannotCreate) && + onlineStatus() == m_protocol->m_ChannelStatusOnline && + channel.lower() == m_nickName.lower()) + { + mTopic = topic; + setProperty(m_protocol->propChannelMembers, members); + setProperty(m_protocol->propChannelTopic, topic); + } +} + +void IRCChannelContact::toggleOperatorActions(bool enabled) +{ + if (enabled) { + actionTopic->setEnabled(true); + } else if (modeEnabled('t')) { + actionTopic->setEnabled(false); + } + + actionModeT->setEnabled(enabled); + actionModeN->setEnabled(enabled); + actionModeS->setEnabled(enabled); + actionModeM->setEnabled(enabled); + actionModeI->setEnabled(enabled); +} + +void IRCChannelContact::slotOnlineStatusChanged(Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus) +{ + Q_UNUSED(oldStatus); + + if (c == account()->myself()) { + if (status.internalStatus() & IRCProtocol::Operator) { + kdDebug(14120) << k_funcinfo << "WE NOW HAVE OP STATUS" << endl; + toggleOperatorActions(true); + } else { + kdDebug(14120) << k_funcinfo << "WE NOW dont HAVE OP STATUS" << endl; + toggleOperatorActions(false); + } + } +} + +void IRCChannelContact::updateStatus() +{ + KIRC::Engine::Status status = kircEngine()->status(); + switch (status) + { + case KIRC::Engine::Idle: + case KIRC::Engine::Connecting: + case KIRC::Engine::Authentifying: + setOnlineStatus(m_protocol->m_ChannelStatusOffline); + break; + case KIRC::Engine::Connected: + case KIRC::Engine::Closing: + setOnlineStatus(m_protocol->m_ChannelStatusOnline); + break; + default: + setOnlineStatus(m_protocol->m_StatusUnknown); + } +} + +void IRCChannelContact::chatSessionDestroyed() +{ + if (manager(Kopete::Contact::CannotCreate)) + { + part(); + Kopete::ContactPtrList contacts = manager()->members(); + + // remove all the users on the channel + for (Kopete::Contact *c = contacts.first(); c; c = contacts.next()) + { + if (c->metaContact()->isTemporary() && + !static_cast<IRCContact*>(c)->isChatting(manager())) + c->deleteLater(); + } + } + + IRCContact::chatSessionDestroyed(); +} + +void IRCChannelContact::initConversation() +{ + kircEngine()->join(m_nickName, password()); +} + +void IRCChannelContact::slotConnectedToServer() +{ + setOnlineStatus(m_protocol->m_ChannelStatusOnline); + if (manager(Kopete::Contact::CannotCreate)) + kircEngine()->join(m_nickName, password()); +} + +void IRCChannelContact::namesList(const QStringList &nicknames) +{ + mInfoTimer->stop(); + mJoinedNicks += nicknames; + slotAddNicknames(); +} + +void IRCChannelContact::endOfNames() +{ + setMode(QString::null); + slotUpdateInfo(); +} + +void IRCChannelContact::slotAddNicknames() +{ + if( !manager(Kopete::Contact::CannotCreate) || mJoinedNicks.isEmpty()) + { + return; + } + + IRCAccount *account = ircAccount(); + + for( uint i = 0; !mJoinedNicks.isEmpty() && i < NICK_BATCH_LENGTH; ++i ) + { + // Pick a nick from the front of the list. + + QString nickToAdd = mJoinedNicks.front(); + QChar firstChar = nickToAdd[0]; + if( firstChar == '@' || firstChar == '%' || firstChar == '+' ) + nickToAdd = nickToAdd.remove(0, 1); + + IRCUserContact *user; + + if ( nickToAdd.lower() != account->mySelf()->nickName().lower() ) + { + //kdDebug(14120) << k_funcinfo << m_nickName << " nick to add: " << nickToAdd << endl; + + user = account->contactManager()->findUser(nickToAdd); + + // If the user is already present in some channel, dont flip the status + // back to online, because the other channels listen to + // onlineStatusChanged() emits, and they would adjust their statuses. + + if (account->contactManager()->findChannelsByMember(user).isEmpty()) { + //kdDebug(14120) << k_funcinfo << "Setting nick ONLINE" << endl; + user->setOnlineStatus(m_protocol->m_UserStatusOnline); + } + } + else + { + // Handling my nick in the list. + user = account->mySelf(); + } + + Kopete::OnlineStatus status; + if ( firstChar == '@' || firstChar == '%' ) + status = m_protocol->m_UserStatusOp; + else if( firstChar == '+') + status = m_protocol->m_UserStatusVoice; + else + status = user->onlineStatus(); + + if( user != account->mySelf() ) + manager()->addContact(user , status, true); + else + manager()->setContactOnlineStatus(user, status); + + mJoinedNicks.pop_front(); + } + + QTimer::singleShot( 0, this, SLOT( slotAddNicknames() ) ); +} + +void IRCChannelContact::channelTopic(const QString &topic) +{ + mTopic = topic; + setProperty( m_protocol->propChannelTopic, mTopic ); + manager()->setDisplayName(caption()); + + if (mTopic.isEmpty()) { + Kopete::Message msg((Kopete::Contact*)this, mMyself, + i18n("Topic for %1 is set empty.").arg(m_nickName), + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW); + appendMessage(msg); + } else { + Kopete::Message msg((Kopete::Contact*)this, mMyself, + i18n("Topic for %1 is %2").arg(m_nickName).arg(mTopic), + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW); + appendMessage(msg); + } +} + +void IRCChannelContact::channelHomePage(const QString &url) +{ + kdDebug(14120) << k_funcinfo << endl; + setProperty( m_protocol->propHomepage, url ); +} + +void IRCChannelContact::join() +{ + if (!manager(Kopete::Contact::CannotCreate) && + onlineStatus().status() == Kopete::OnlineStatus::Online) + { + kdDebug() << k_funcinfo << "My nickname:" << m_nickName << endl; + kdDebug() << k_funcinfo << "My manager:" << manager(Kopete::Contact::CannotCreate) << endl; + if( manager(Kopete::Contact::CannotCreate) ) + kdDebug() << k_funcinfo << "My view:" << manager(Kopete::Contact::CannotCreate)->view(false) << endl; + startChat(); + } + + if (manager()) { + connect(manager(), + SIGNAL(onlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus &, + const Kopete::OnlineStatus &)), + SLOT(slotOnlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus &, + const Kopete::OnlineStatus &))); + } +} + +void IRCChannelContact::partAction() +{ + if (manager()) + manager()->view()->closeView(); +} + +void IRCChannelContact::part() +{ + if (manager() && !kircEngine()->isDisconnected()) + kircEngine()->part(m_nickName, ircAccount()->defaultPart()); +} + +void IRCChannelContact::slotIncomingUserIsAway( const QString &nick, const QString & ) +{ + IRCAccount *account = ircAccount(); + + if( nick.lower() == account->mySelf()->nickName().lower() ) + { + IRCUserContact *c = account->mySelf(); + if (manager() && manager()->members().contains(c)) + { + Kopete::OnlineStatus status = manager()->contactOnlineStatus(c); + if (status == m_protocol->m_UserStatusOp) + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOpAway ); + else if (status == m_protocol->m_UserStatusOpAway) + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOp); + else if (status == m_protocol->m_UserStatusVoice) + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusVoiceAway); + else if (status == m_protocol->m_UserStatusVoiceAway) + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusVoice); + else if (status == m_protocol->m_UserStatusAway) + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOnline); + else + manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusAway); + } + } +} + +void IRCChannelContact::userJoinedChannel(const QString &nickname) +{ + IRCAccount *account = ircAccount(); + + if (nickname.lower() == account->mySelf()->nickName().lower()) + { + kdDebug() << k_funcinfo << "Me:" << this << endl; + kdDebug() << k_funcinfo << "My nickname:" << m_nickName << endl; + kdDebug() << k_funcinfo << "My manager:" << manager(Kopete::Contact::CannotCreate) << endl; + + if (manager(Kopete::Contact::CannotCreate)) + kdDebug() << k_funcinfo << "My view:" << manager(Kopete::Contact::CannotCreate)->view(false) << endl; + + Kopete::Message msg((Kopete::Contact *)this, mMyself, + i18n("You have joined channel %1").arg(m_nickName), + Kopete::Message::Internal, Kopete::Message::PlainText, + CHAT_VIEW); + msg.setImportance( Kopete::Message::Low); //set the importance manualy to low + appendMessage(msg); + } + else + { + // If we have lag or huge channels, we might receive a JOIN after we have left a channel. + if (!manager()) + return; + + IRCUserContact *contact = account->contactManager()->findUser( nickname ); + contact->setOnlineStatus( m_protocol->m_UserStatusOnline ); + manager()->addContact((Kopete::Contact *)contact, true); + Kopete::Message msg((Kopete::Contact *)this, mMyself, + i18n("User <b>%1</b> joined channel %2").arg(nickname).arg(m_nickName), + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW); + msg.setImportance( Kopete::Message::Low); //set the importance manualy to low + manager()->appendMessage(msg); + } +} + +void IRCChannelContact::userPartedChannel(const QString &nickname,const QString &reason) +{ + IRCAccount *account = ircAccount(); + + if (nickname.lower() != account->engine()->nickName().lower()) + { + Kopete::Contact *c = locateUser( nickname ); + if ( c ) + { + manager()->removeContact( c, Kopete::Message::unescape(reason) ); + if( c->metaContact()->isTemporary() && !static_cast<IRCContact*>(c)->isChatting( manager(Kopete::Contact::CannotCreate) ) ) + c->deleteLater(); + } + } +} + +void IRCChannelContact::userKicked(const QString &nick, const QString &nickKicked, const QString &reason) +{ + IRCAccount *account = ircAccount(); + + if( nickKicked.lower() != account->engine()->nickName().lower() ) + { + Kopete::Contact *c = locateUser( nickKicked ); + if (c) + { + QString r; + + if ((reason != nick) && (reason != nickKicked)) { + r = i18n( "%1 was kicked by %2. Reason: %3" ).arg(nickKicked, nick, reason); + } else { + r = i18n( "%1 was kicked by %2." ).arg(nickKicked, nick); + } + + manager()->removeContact( c, r ); + Kopete::Message msg( this, mMyself, r, + Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW); + msg.setImportance(Kopete::Message::Low); + appendMessage(msg); + + if( c->metaContact()->isTemporary() && + !static_cast<IRCContact*>(c)->isChatting( manager() ) ) + c->deleteLater(); + } + } + else + { + QString r; + + if ((reason != nick) && (reason != nickKicked)) { + r = i18n( "You were kicked from %1 by %2. Reason: %3" ).arg(m_nickName, nickKicked, reason); + } else { + r = i18n( "You were kicked from %1 by %2." ).arg(m_nickName, nickKicked); + } + + KMessageBox::error(Kopete::UI::Global::mainWidget(), r, i18n("IRC Plugin")); + manager()->view()->closeView(); + } +} + +void IRCChannelContact::setTopic(const QString &topic) +{ + IRCAccount *account = ircAccount(); + + if (manager(Kopete::Contact::CannotCreate)) + { + if( manager()->contactOnlineStatus( manager()->myself() ) == + m_protocol->m_UserStatusOp || !modeEnabled('t') ) + { + bool okPressed = true; + QString newTopic = topic; + if( newTopic.isNull() ) + newTopic = KInputDialog::getText( i18n("New Topic"), i18n("Enter the new topic:"), + Kopete::Message::unescape(mTopic), &okPressed, 0L ); + + if( okPressed ) + { + mTopic = newTopic; + kircEngine()->topic(m_nickName, newTopic); + } + } + else + { + Kopete::Message msg(account->myServer(), manager()->members(), + i18n("You must be a channel operator on %1 to do that.").arg(m_nickName), + Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW); + manager()->appendMessage(msg); + } + } +} + +void IRCChannelContact::topicChanged(const QString &nick, const QString &newtopic) +{ + IRCAccount *account = ircAccount(); + + mTopic = newtopic; + setProperty( m_protocol->propChannelTopic, mTopic ); + manager()->setDisplayName( caption() ); + Kopete::Message msg(account->myServer(), mMyself, + i18n("%1 has changed the topic to: %2").arg(nick).arg(newtopic), + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW); + msg.setImportance(Kopete::Message::Low); //set the importance manualy to low + appendMessage(msg); +} + +void IRCChannelContact::topicUser(const QString &nick, const QDateTime &time) +{ + IRCAccount *account = ircAccount(); + + Kopete::Message msg(account->myServer(), mMyself, + i18n("Topic set by %1 at %2").arg(nick).arg( + KGlobal::locale()->formatDateTime(time, true) + ), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW); + msg.setImportance(Kopete::Message::Low); //set the importance manualy to low + appendMessage(msg); +} + +void IRCChannelContact::incomingModeChange( const QString &nick, const QString &mode ) +{ + Kopete::Message msg(this, mMyself, i18n("%1 sets mode %2 on %3").arg(nick).arg(mode).arg(m_nickName), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW); + msg.setImportance( Kopete::Message::Low); //set the importance manualy to low + appendMessage(msg); + + bool inParams = false; + bool modeEnabled = false; + QString params = QString::null; + for( uint i=0; i < mode.length(); i++ ) + { + switch( mode[i] ) + { + case '+': + modeEnabled = true; + break; + + case '-': + modeEnabled = false; + break; + + case ' ': + inParams = true; + break; + default: + if( inParams ) + params.append( mode[i] ); + else + toggleMode( mode[i], modeEnabled, false ); + break; + } + } +} + +void IRCChannelContact::incomingChannelMode( const QString &mode, + const QString &/*params*/ ) +{ + for( uint i=1; i < mode.length(); i++ ) + { + if( mode[i] != 'l' && mode[i] != 'k' ) + toggleMode( mode[i], true, false ); + } +} + +void IRCChannelContact::setMode(const QString &mode) +{ + if (manager(Kopete::Contact::CannotCreate)) + kircEngine()->mode(m_nickName, mode); +} + +void IRCChannelContact::slotModeChanged() +{ + toggleMode( 't', actionModeT->isChecked(), true ); + toggleMode( 'n', actionModeN->isChecked(), true ); + toggleMode( 's', actionModeS->isChecked(), true ); + toggleMode( 'm', actionModeM->isChecked(), true ); + toggleMode( 'i', actionModeI->isChecked(), true ); +} + +void IRCChannelContact::failedChanBanned() +{ + manager()->deleteLater(); + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n("<qt>You can not join %1 because you have been banned.</qt>").arg(m_nickName), + i18n("IRC Plugin") ); +} + +void IRCChannelContact::failedChanInvite() +{ + manager()->deleteLater(); + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n("<qt>You can not join %1 because it is set to invite only, and no one has invited you.</qt>").arg(m_nickName), i18n("IRC Plugin") ); +} + +void IRCChannelContact::failedChanFull() +{ + manager()->deleteLater(); + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n("<qt>You can not join %1 because it has reached its user limit.</qt>").arg(m_nickName), + i18n("IRC Plugin") ); +} + +void IRCChannelContact::failedChankey() +{ + bool ok; + QString diaPassword = KInputDialog::getText( i18n( "IRC Plugin" ), + i18n( "Please enter key for channel %1: ").arg(m_nickName), + QString::null, + &ok ); + + if ( !ok ) + manager()->deleteLater(); + else + { + setPassword(diaPassword); + kircEngine()->join(m_nickName, password()); + } +} + +void IRCChannelContact::toggleMode( QChar mode, bool enabled, bool update ) +{ + if( manager(Kopete::Contact::CannotCreate) ) + { + switch( mode ) + { + case 't': + actionModeT->setChecked( enabled ); + + // If someones sets +t and we're not channel operators, disable the action. + if (enabled && !(manager()->contactOnlineStatus(ircAccount()->myself()).internalStatus() & IRCProtocol::Operator)) { + actionTopic->setEnabled( false ); + } else { + actionTopic->setEnabled( true ); + } + break; + case 'n': + actionModeN->setChecked( enabled ); + break; + case 's': + actionModeS->setChecked( enabled ); + break; + case 'm': + actionModeM->setChecked( enabled ); + break; + case 'i': + actionModeI->setChecked( enabled ); + break; + } + } + + if( update ) + { + if( modeMap[mode] != enabled ) + { + if( enabled ) + setMode( QString::fromLatin1("+") + mode ); + else + setMode( QString::fromLatin1("-") + mode ); + } + } + + modeMap[mode] = enabled; +} + +bool IRCChannelContact::modeEnabled( QChar mode, QString *value ) +{ + if( !value ) + return modeMap[mode]; + + return false; +} + +QPtrList<KAction> *IRCChannelContact::customContextMenuActions() +{ + QPtrList<KAction> *mCustomActions = new QPtrList<KAction>(); + if( !actionJoin ) + { + actionJoin = new KAction(i18n("&Join"), 0, this, SLOT(join()), this, "actionJoin"); + actionPart = new KAction(i18n("&Part"), 0, this, SLOT(partAction()), this, "actionPart"); + actionTopic = new KAction(i18n("Change &Topic..."), 0, this, SLOT(setTopic()), this, "actionTopic"); + actionModeMenu = new KActionMenu(i18n("Channel Modes"), 0, this, "actionModeMenu"); + + if( !property(m_protocol->propHomepage).value().isNull() ) + { + actionHomePage = new KAction( i18n("Visit &Homepage"), 0, this, + SLOT(slotHomepage()), this, "actionHomepage"); + } + else if( actionHomePage ) + { + delete actionHomePage; + } + + actionModeMenu->insert( actionModeT ); + actionModeMenu->insert( actionModeN ); + actionModeMenu->insert( actionModeS ); + actionModeMenu->insert( actionModeM ); + actionModeMenu->insert( actionModeI ); + actionModeMenu->setEnabled( true ); + + codecAction = new KCodecAction( i18n("&Encoding"), 0, this, "selectcharset" ); + connect( codecAction, SIGNAL( activated( const QTextCodec * ) ), + this, SLOT( setCodec( const QTextCodec *) ) ); + codecAction->setCodec( codec() ); + } + + mCustomActions->append( actionJoin ); + mCustomActions->append( actionPart ); + mCustomActions->append( actionTopic ); + mCustomActions->append( actionModeMenu ); + mCustomActions->append( codecAction ); + if( actionHomePage ) + mCustomActions->append( actionHomePage ); + + bool isOperator = manager(Kopete::Contact::CannotCreate) && + (manager()->contactOnlineStatus(ircAccount()->myself()).internalStatus() & IRCProtocol::Operator); + + actionJoin->setEnabled( !manager(Kopete::Contact::CannotCreate) ); + actionPart->setEnabled( manager(Kopete::Contact::CannotCreate) ); + actionTopic->setEnabled( manager(Kopete::Contact::CannotCreate) && ( !modeEnabled('t') || isOperator ) ); + + toggleOperatorActions(isOperator); + + return mCustomActions; +} + +void IRCChannelContact::slotHomepage() +{ + QString homePage = property(m_protocol->propHomepage).value().toString(); + if( !homePage.isEmpty() ) + { + new KRun( KURL( homePage ), 0, false); + } +} + +const QString IRCChannelContact::caption() const +{ + QString cap = QString::fromLatin1("%1 @ %2").arg(m_nickName).arg(kircEngine()->currentHost()); + if(!mTopic.isEmpty()) + cap.append( QString::fromLatin1(" - %1").arg(Kopete::Message::unescape(mTopic)) ); + + return cap; +} + +void IRCChannelContact::privateMessage(IRCContact *from, IRCContact *to, const QString &message) +{ + if(to == this) + { + Kopete::Message msg(from, manager()->members(), message, Kopete::Message::Inbound, + Kopete::Message::RichText, CHAT_VIEW); + appendMessage(msg); + } +} + +void IRCChannelContact::newAction(const QString &from, const QString &action) +{ + IRCAccount *account = ircAccount(); + + IRCUserContact *f = account->contactManager()->findUser(from); + Kopete::Message::MessageDirection dir = + (f == account->mySelf()) ? Kopete::Message::Outbound : Kopete::Message::Inbound; + Kopete::Message msg(f, manager()->members(), action, dir, Kopete::Message::RichText, + CHAT_VIEW, Kopete::Message::TypeAction); + appendMessage(msg); +} + +#include "ircchannelcontact.moc" diff --git a/kopete/protocols/irc/ircchannelcontact.h b/kopete/protocols/irc/ircchannelcontact.h new file mode 100644 index 00000000..15a72e17 --- /dev/null +++ b/kopete/protocols/irc/ircchannelcontact.h @@ -0,0 +1,156 @@ +/* + ircchannelcontact.h - IRC Channel Contact + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCCHANNELCONTACT_H +#define IRCCHANNELCONTACT_H + +#include "irccontact.h" + +class KActionCollection; +class KAction; +class KActionMenu; +class KCodecAction; +class KToggleAction; + +namespace Kopete { class MetaContact; } +namespace Kopete { class ChatSession; } +namespace Kopete { class Message; } +class KopeteView; + +class IRCAccount; +class IRCContactManager; + +/** + * @author Jason Keirstead <jason@keirstead.org> + * + * This class is the @ref Kopete::Contact object representing IRC Channels, not users. + * It is derived from IRCContact where much of its functionality is shared with @ref IRCUserContact. + */ +class IRCChannelContact + : public IRCContact +{ + friend class IRCSignalMapper; + + Q_OBJECT + +public: + IRCChannelContact(IRCContactManager *, const QString &channel, Kopete::MetaContact *metac); + ~IRCChannelContact(); + + /** + * Returns the current topic for this channel. + */ + const QString &topic() const { return mTopic; }; + + /* Set password for a channel */ + void setPassword(const QString &password) { mPassword = password; } + /* Get password for a channel */ + const QString &password() const { return mPassword; } + + /** + * Returns if a mode is enabled for this channel. + * @param mode The mode you want to check ( 't', 'n', etc. ) + * @param value This is a pointer to a QString which is set to + * the value of the mode if it has one. Example, the mode 'l' or + * the mode 'k'. If the mode has no such value then the pointer + * is always returned null. + */ + bool modeEnabled( QChar mode, QString *value = 0 ); + + // Kopete::Contact stuff + virtual QPtrList<KAction> *customContextMenuActions(); + virtual const QString caption() const; + + //Methods handled by the signal mapper + void userJoinedChannel(const QString &user); + void userPartedChannel(const QString &user, const QString &reason); + void userKicked(const QString &nick, const QString &nickKicked, const QString &reason); + void channelTopic(const QString &topic); + void channelHomePage(const QString &url); + void topicChanged(const QString &nick, const QString &newtopic); + void topicUser(const QString &nick, const QDateTime &time); + void namesList(const QStringList &nicknames); + void endOfNames(); + void incomingModeChange(const QString &nick, const QString &mode); + void incomingChannelMode(const QString &mode, const QString ¶ms ); + void failedChankey(); + void failedChanBanned(); + void failedChanInvite(); + void failedChanFull(); + void newAction(const QString &from, const QString &action); + +public slots: + void updateStatus(); + + /** + * Sets the topic of this channel + * @param topic The topic you want set + */ + void setTopic( const QString &topic = QString::null ); + + /** + * Sets or unsets a mode on this channel + * @param mode The full text of the mode change you want performed + */ + void setMode( const QString &mode = QString::null ); + + void part(); + void partAction(); + void join(); + +protected slots: + void chatSessionDestroyed(); + + virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message); + virtual void initConversation(); + +private slots: + void slotIncomingUserIsAway( const QString &nick, const QString &reason ); + void slotModeChanged(); + void slotAddNicknames(); + void slotConnectedToServer(); + void slotUpdateInfo(); + void slotHomepage(); + void slotChannelListed(const QString &channel, uint members, const QString &topic); + void slotOnlineStatusChanged(Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus); + +private: + KAction *actionJoin; + KAction *actionPart; + KAction *actionTopic; + KAction *actionHomePage; + KActionMenu *actionModeMenu; + KCodecAction *codecAction; + + KToggleAction *actionModeT; // Only Operators Can Change Topic + KToggleAction *actionModeN; // No Outside Messages + KToggleAction *actionModeS; // Secret + KToggleAction *actionModeI; // Invite Only + KToggleAction *actionModeM; // Moderated + + QString mTopic; + QString mPassword; + QStringList mJoinedNicks; + QMap<QString, bool> modeMap; + QTimer *mInfoTimer; + + void toggleMode( QChar mode, bool enabled, bool update ); + void toggleOperatorActions( bool enabled ); +}; + +#endif diff --git a/kopete/protocols/irc/ircchatui.rc b/kopete/protocols/irc/ircchatui.rc new file mode 100644 index 00000000..9c1b9dbb --- /dev/null +++ b/kopete/protocols/irc/ircchatui.rc @@ -0,0 +1,10 @@ +<!DOCTYPE kpartgui> +<kpartgui version="26" name="kopetechatwindow"> + <MenuBar> + <Menu name="irc" > + <text>IRC</text> + <ActionList name="irccontactactionlist" /> + </Menu> + </MenuBar> + +</kpartgui> diff --git a/kopete/protocols/irc/irccontact.cpp b/kopete/protocols/irc/irccontact.cpp new file mode 100644 index 00000000..64f89322 --- /dev/null +++ b/kopete/protocols/irc/irccontact.cpp @@ -0,0 +1,425 @@ +/* + irccontact.cpp - IRC Contact + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + + Kopete (c) 2002-2005 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 <kdebug.h> +#include <klocale.h> +#include <qregexp.h> + +#include <qtimer.h> +#include <qtextcodec.h> + +#include "ircaccount.h" +#include "kopeteglobal.h" +#include "kopeteuiglobal.h" +#include "kopetemetacontact.h" +#include "kopeteview.h" +#include "ircusercontact.h" +#include "irccontact.h" +#include "ircprotocol.h" +#include "ircservercontact.h" +#include "irccontactmanager.h" +#include "ksparser.h" + +IRCContact::IRCContact(IRCAccount *account, KIRC::EntityPtr entity, Kopete::MetaContact *metac, const QString& icon) + : Kopete::Contact(account, entity->name(), metac, icon), + m_chatSession(0) +{ +} + +IRCContact::IRCContact(IRCContactManager *contactManager, const QString &nick, Kopete::MetaContact *metac, const QString& icon) + : Kopete::Contact(contactManager->account(), nick, metac, icon), + m_nickName(nick), + m_chatSession(0) +{ + KIRC::Engine *engine = kircEngine(); + + // Contact list display name + setProperty( Kopete::Global::Properties::self()->nickName(), m_nickName ); + + // IRCContactManager stuff + QObject::connect(contactManager, SIGNAL(privateMessage(IRCContact *, IRCContact *, const QString &)), + this, SLOT(privateMessage(IRCContact *, IRCContact *, const QString &))); + + // Kopete::ChatSessionManager stuff + mMyself.append( static_cast<Kopete::Contact*>( this ) ); + + // KIRC stuff + QObject::connect(engine, SIGNAL(incomingNickChange(const QString &, const QString &)), + this, SLOT( slotNewNickChange(const QString&, const QString&))); + QObject::connect(engine, SIGNAL(successfullyChangedNick(const QString &, const QString &)), + this, SLOT(slotNewNickChange(const QString &, const QString &))); + QObject::connect(engine, SIGNAL(incomingQuitIRC(const QString &, const QString &)), + this, SLOT( slotUserDisconnected(const QString&, const QString&))); + + QObject::connect(engine, SIGNAL(statusChanged(KIRC::Engine::Status)), + this, SLOT(updateStatus())); + + engine->setCodec( m_nickName, codec() ); +} + +IRCContact::~IRCContact() +{ +// kdDebug(14120) << k_funcinfo << m_nickName << endl; + if (metaContact() && metaContact()->isTemporary() && !isChatting(m_chatSession)) + metaContact()->deleteLater(); + + emit destroyed(this); +} + +IRCAccount *IRCContact::ircAccount() const +{ + return static_cast<IRCAccount *>(account()); +} + +KIRC::Engine *IRCContact::kircEngine() const +{ + return ircAccount()->engine(); +} + +bool IRCContact::isReachable() +{ + if (onlineStatus().status() != Kopete::OnlineStatus::Offline && + onlineStatus().status() != Kopete::OnlineStatus::Unknown) + return true; + + return false; +} + +const QString IRCContact::caption() const +{ + return QString::null; +} +/* +const QString IRCContact::formatedName() const +{ + return QString::null; +} +*/ +void IRCContact::updateStatus() +{ +} + +void IRCContact::privateMessage(IRCContact *, IRCContact *, const QString &) +{ +} + +void IRCContact::setCodec(const QTextCodec *codec) +{ + kircEngine()->setCodec(m_nickName, codec); + metaContact()->setPluginData(m_protocol, QString::fromLatin1("Codec"), QString::number(codec->mibEnum())); +} + +const QTextCodec *IRCContact::codec() +{ + QString codecId = metaContact()->pluginData(m_protocol, QString::fromLatin1("Codec")); + QTextCodec *codec = ircAccount()->codec(); + + if( !codecId.isEmpty() ) + { + bool test = true; + uint mib = codecId.toInt(&test); + if (test) + codec = QTextCodec::codecForMib(mib); + else + codec = QTextCodec::codecForName(codecId.latin1()); + } + + if( !codec ) + return kircEngine()->codec(); + + return codec; +} + +Kopete::ChatSession *IRCContact::manager(Kopete::Contact::CanCreateFlags canCreate) +{ + IRCAccount *account = ircAccount(); + KIRC::Engine *engine = kircEngine(); + + if (canCreate == Kopete::Contact::CanCreate && !m_chatSession) + { + if( engine->status() == KIRC::Engine::Idle && dynamic_cast<IRCServerContact*>(this) == 0 ) + account->connect(); + + m_chatSession = Kopete::ChatSessionManager::self()->create(account->myself(), mMyself, account->protocol()); + m_chatSession->setDisplayName(caption()); + + QObject::connect(m_chatSession, SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession *)), + this, SLOT(slotSendMsg(Kopete::Message&, Kopete::ChatSession *))); + QObject::connect(m_chatSession, SIGNAL(closing(Kopete::ChatSession *)), + this, SLOT(chatSessionDestroyed())); + + initConversation(); + } + + return m_chatSession; +} + +void IRCContact::chatSessionDestroyed() +{ + m_chatSession = 0; + + if (metaContact()->isTemporary() && !isChatting()) + deleteLater(); +} + +void IRCContact::slotUserDisconnected(const QString &user, const QString &reason) +{ + if (m_chatSession) + { + QString nickname = user.section('!', 0, 0); + Kopete::Contact *c = locateUser( nickname ); + if ( c ) + { + m_chatSession->removeContact(c, i18n("Quit: \"%1\" ").arg(reason), Kopete::Message::RichText); + c->setOnlineStatus(m_protocol->m_UserStatusOffline); + } + } +} + +void IRCContact::setNickName( const QString &nickname ) +{ + kdDebug(14120) << k_funcinfo << m_nickName << " changed to " << nickname << endl; + m_nickName = nickname; + Kopete::Contact::setNickName( nickname ); +} + +void IRCContact::slotNewNickChange(const QString &oldnickname, const QString &newnickname) +{ + IRCAccount *account = ircAccount(); + + IRCContact *user = static_cast<IRCContact*>( locateUser(oldnickname) ); + if( user ) + { + user->setNickName( newnickname ); + + //If the user is in our contact list, then change the notify list nickname + if (!user->metaContact()->isTemporary()) + { + account->contactManager()->removeFromNotifyList( oldnickname ); + account->contactManager()->addToNotifyList( newnickname ); + } + } +} + +void IRCContact::slotSendMsg(Kopete::Message &message, Kopete::ChatSession *) +{ + QString htmlString = message.escapedBody(); + + // Messages we get with RichText enabled: + // + // Hello world in bold and color: + // <span style="font-weight:600;color:#403897">Hello World</span> + // + // Two-liner in color: + // <span style="color:#403897">Hello<br />World</span> + + if (htmlString.find(QString::fromLatin1("</span")) > -1) + { + QRegExp findTags( QString::fromLatin1("<span style=\"(.*)\">(.*)</span>") ); + findTags.setMinimal( true ); + int pos = 0; + + while (pos >= 0) + { + pos = findTags.search(htmlString); + if (pos > -1) + { + QString styleHTML = findTags.cap(1); + QString replacement = findTags.cap(2); + QStringList styleAttrs = QStringList::split(';', styleHTML); + + for (QStringList::Iterator attrPair = styleAttrs.begin(); attrPair != styleAttrs.end(); ++attrPair) + { + QString attribute = (*attrPair).section(':',0,0); + QString value = (*attrPair).section(':',1); + + if( attribute == QString::fromLatin1("color") ) + { + int ircColor = KSParser::colorForHTML( value ); + if( ircColor > -1 ) + replacement.prepend( QString( QChar(0x03) ).append( QString::number(ircColor) ) ).append( QChar( 0x03 ) ); + } + else if( attribute == QString::fromLatin1("font-weight") && + value == QString::fromLatin1("600") ) { + // Bolding + replacement.prepend( QChar(0x02) ).append( QChar(0x02) ); + } + else if( attribute == QString::fromLatin1("text-decoration") && + value == QString::fromLatin1("underline") ) { + replacement.prepend( QChar(31) ).append( QChar(31) ); + } + } + + htmlString = htmlString.left( pos ) + replacement + htmlString.mid( pos + findTags.matchedLength() ); + } + } + } + + htmlString = Kopete::Message::unescape(htmlString); + + QStringList messages = QStringList::split( '\n', htmlString ); + + for( QStringList::Iterator it = messages.begin(); it != messages.end(); ++it ) + { + // Dont use the resulting string(s). The problem is that we'd have to parse them + // back to format that would be suitable for appendMessage(). + // + // TODO: If the given message was plaintext, we could easily show what was + // actually sent. + + sendMessage(*it); + } + + if (message.requestedPlugin() != CHAT_VIEW) { + Kopete::Message msg(message.from(), message.to(), message.escapedBody(), message.direction(), + Kopete::Message::RichText, CHAT_VIEW, message.type()); + + msg.setBg(QColor()); + msg.setFg(QColor()); + + appendMessage(msg); + } else { + // Lets not modify the given message object. + Kopete::Message msg = message; + msg.setBg(QColor()); + appendMessage(msg); + } + + manager(Kopete::Contact::CanCreate)->messageSucceeded(); +} + +QStringList IRCContact::sendMessage( const QString &msg ) +{ + QStringList messages; + + QString newMessage = msg; + + // IRC limits the message size to 512 characters. So split the given + // message into pieces. + // + // This can of course give nasty results, but most of us dont write + // that long lines anyway ;-)... And this is how other clients also + // seem to behave. + + int l = 500 - m_nickName.length(); + + do { + messages.append(newMessage.mid(0, l)); + newMessage.remove(0, l); + } while (!newMessage.isEmpty()); + + for (QStringList::const_iterator it = messages.begin(); + it != messages.end(); ++it) + kircEngine()->privmsg(m_nickName, *it); + + return messages; +} + +Kopete::Contact *IRCContact::locateUser(const QString &nick) +{ + IRCAccount *account = ircAccount(); + + if (m_chatSession) + { + if( nick == account->mySelf()->nickName() ) + return account->mySelf(); + else + { + Kopete::ContactPtrList mMembers = m_chatSession->members(); + for (Kopete::Contact *it = mMembers.first(); it; it = mMembers.next()) + { + if (static_cast<IRCContact*>(it)->nickName() == nick) + return it; + } + } + } + return 0; +} + +bool IRCContact::isChatting(const Kopete::ChatSession *avoid) const +{ + IRCAccount *account = ircAccount(); + + if (!account) + return false; + + QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions(); + for (QValueList<Kopete::ChatSession*>::Iterator it= sessions.begin(); it!=sessions.end() ; ++it) + { + if( (*it) != avoid && (*it)->account() == account && + (*it)->members().contains(this) ) + { + return true; + } + } + return false; +} + +void IRCContact::deleteContact() +{ + kdDebug(14120) << k_funcinfo << m_nickName << endl; + + delete m_chatSession; + + if (!isChatting()) + { + kdDebug(14120) << k_funcinfo << "will delete " << m_nickName << endl; + Kopete::Contact::deleteContact(); + } + else + { + metaContact()->removeContact(this); + Kopete::MetaContact *m = new Kopete::MetaContact(); + m->setTemporary(true); + setMetaContact(m); + } +} + +void IRCContact::appendMessage(Kopete::Message &msg) +{ + manager(Kopete::Contact::CanCreate)->appendMessage(msg); +} + +KopeteView *IRCContact::view() +{ + if (m_chatSession) + return m_chatSession->view(false); + return 0L; +} +void IRCContact::serialize(QMap<QString, QString> & /*serializedData*/, QMap<QString, QString> &addressBookData) +{ + // write the + addressBookData[ protocol()->addressBookIndexField() ] = ( contactId() + QChar(0xE120) + account()->accountId() ); +} + +void IRCContact::receivedMessage( KIRC::Engine::ServerMessageType type, + const KIRC::EntityPtr &from, + const KIRC::EntityPtrList &to, + const QString &msg) +{ + if (to.contains(m_entity)) + { + IRCContact *fromContact = ircAccount()->getContact(from); + Kopete::Message message(fromContact, manager()->members(), msg, Kopete::Message::Inbound, + Kopete::Message::RichText, CHAT_VIEW); + appendMessage(message); + } +} + +#include "irccontact.moc" diff --git a/kopete/protocols/irc/irccontact.h b/kopete/protocols/irc/irccontact.h new file mode 100644 index 00000000..058315fb --- /dev/null +++ b/kopete/protocols/irc/irccontact.h @@ -0,0 +1,153 @@ +/* + irccontact.h - IRC Contact + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002-2005 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCCONTACT_H +#define IRCCONTACT_H + +#include "kircengine.h" +#include "kircentity.h" + +#include "kopetecontact.h" +#include "kopetemessage.h" + +#include <qptrlist.h> +#include <qmap.h> + +class IRCProtocol; +class IRCAccount; +class IRCContactManager; + +namespace KIRC +{ +class Engine; +} + +namespace Kopete +{ +class ChatSession; +class MetaContact; +} + +class KopeteView; + +class QTextCodec; + +/** + * @author Jason Keirstead <jason@keirstead.org> + * @author Michel Hermier <michel.hermier@wanadoo.fr> + * + * This class is the base class for @ref IRCUserContact and @ref IRCChannelContact. + * Common routines and signal connections that are required for both types of + * contacts reside here, to avoid code duplication between these two classes. + */ +class IRCContact + : public Kopete::Contact +{ + Q_OBJECT + +public: + IRCContact(IRCAccount *account, KIRC::EntityPtr entity, Kopete::MetaContact *metac, const QString& icon = QString::null); + IRCContact(IRCContactManager *contactManager, const QString &nick, Kopete::MetaContact *metac, const QString& icon = QString::null); + virtual ~IRCContact(); + + IRCAccount *ircAccount() const; + KIRC::Engine *kircEngine() const; + + /** + * Sets the nickname of this contact. The nickname is distinct from the displayName + * in case trackNameChanges is disabled. + */ + void setNickName(const QString &nickname); + + /** + * Returns the nickname / channel name + */ + const QString &nickName() const { return m_nickName; } + + /** + * This function attempts to find the nickname specified within the current chat + * session. Returns a pointer to that IRCUserContact, or 0L if the user does not + * exist in this session. More useful for channels. Calling IRCChannelContact::locateUser() + * for example tells you if a user is in a certain channel. + */ + Kopete::Contact *locateUser( const QString &nickName ); + + virtual bool isReachable(); + + /** + * return true if the contact is in a chat. false if the contact is in no chats + * that loop over all manager, and checks the presence of the user + */ + bool isChatting( const Kopete::ChatSession *avoid = 0L ) const; + + virtual const QString caption() const; +// virtual const QString formatedName() const; + + virtual Kopete::ChatSession *manager(Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate); + + virtual void appendMessage( Kopete::Message & ); + + const QTextCodec *codec(); + + KopeteView *view(); + + /** + * We serialise the contactId and the server group in 'contactId' + * so that other IRC programs reading this from KAddressBook have a chance of figuring + * which server the contact relates to + */ + virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData ); + +signals: + void destroyed(IRCContact *self); + +public slots: + void setCodec( const QTextCodec *codec ); + virtual void updateStatus(); + +protected slots: + virtual void slotSendMsg(Kopete::Message &message, Kopete::ChatSession *); + QStringList sendMessage( const QString &msg ); + + virtual void chatSessionDestroyed(); + + void slotNewNickChange( const QString &oldnickname, const QString &newnickname); + void slotUserDisconnected( const QString &nickname, const QString &reason); + + virtual void deleteContact(); + virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message); + virtual void initConversation() {}; + + void receivedMessage( KIRC::Engine::ServerMessageType type, + const KIRC::EntityPtr &from, + const KIRC::EntityPtrList &to, + const QString &msg); + +protected: + KIRC::EntityPtr m_entity; + + QString m_nickName; + Kopete::ChatSession *m_chatSession; + + QPtrList<Kopete::Contact> mMyself; + Kopete::Message::MessageDirection execDir; +}; + +#endif diff --git a/kopete/protocols/irc/irccontactmanager.cpp b/kopete/protocols/irc/irccontactmanager.cpp new file mode 100644 index 00000000..7808668b --- /dev/null +++ b/kopete/protocols/irc/irccontactmanager.cpp @@ -0,0 +1,297 @@ +/* + irccontactmanager.cpp - Manager of IRC Contacts + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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 "ircusercontact.h" +#include "ircaccount.h" +#include "irccontactmanager.h" +#include "ircprotocol.h" +#include "ircsignalhandler.h" + +#include "ircservercontact.h" +#include "ircchannelcontact.h" + +#include "kircengine.h" + +#include <kopeteaccountmanager.h> +#include <kopetemetacontact.h> +#include <kopetecontactlist.h> +#include <kopeteview.h> + +#include <kconfig.h> +#include <kstandarddirs.h> + +#include <qtimer.h> + +IRCContactManager::IRCContactManager(const QString &nickName, IRCAccount *account, const char *name) + : QObject(account, name), + m_channels( QDict<IRCChannelContact>( 17, false ) ), + m_users( QDict<IRCUserContact>( 577, false ) ), + m_account( account ) +{ + m_mySelf = findUser(nickName); + + Kopete::MetaContact *m = new Kopete::MetaContact(); +// m->setTemporary( true ); + m_myServer = new IRCServerContact(this, account->networkName(), m); + + QObject::connect(account->engine(), SIGNAL(incomingMessage(const QString &, const QString &, const QString &)), + this, SLOT(slotNewMessage(const QString &, const QString &, const QString &))); + + QObject::connect(account->engine(), SIGNAL(incomingPrivMessage(const QString &, const QString &, const QString &)), + this, SLOT(slotNewPrivMessage(const QString &, const QString &, const QString &))); + + QObject::connect(account->engine(), SIGNAL(incomingNickChange(const QString &, const QString &)), + this, SLOT( slotNewNickChange(const QString&, const QString&))); + + QObject::connect(account->engine(), SIGNAL(successfullyChangedNick(const QString &, const QString &)), + this, SLOT( slotNewNickChange(const QString &, const QString &))); + + QObject::connect(account->engine(), SIGNAL(incomingUserOnline(const QString &)), + this, SLOT( slotIsonRecieved())); + + QObject::connect(Kopete::ContactList::self(), SIGNAL(metaContactAdded( Kopete::MetaContact * )), + this, SLOT( slotContactAdded( Kopete::MetaContact* ))); + + socketTimeout = 15000; + QString timeoutPath = locate( "config", "kioslaverc" ); + if( !timeoutPath.isEmpty() ) + { + KConfig config( timeoutPath ); + socketTimeout = config.readNumEntry( "ReadTimeout", 15 ) * 1000; + } + + m_NotifyTimer = new QTimer(this); + QObject::connect(m_NotifyTimer, SIGNAL(timeout()), + this, SLOT(checkOnlineNotifyList())); + m_NotifyTimer->start(30000); // check online every 30sec + + new IRCSignalHandler(this); +} + +void IRCContactManager::slotNewNickChange(const QString &oldnick, const QString &newnick) +{ + IRCUserContact *c = m_users[ oldnick ]; + if( c ) + { + m_users.insert(newnick, c); + m_users.remove(oldnick); + } +} + +void IRCContactManager::slotNewMessage(const QString &originating, const QString &channel, const QString &message) +{ + IRCContact *from = findUser(originating); + IRCChannelContact *to = findChannel(channel); + emit privateMessage(from, to, message); +} + +void IRCContactManager::slotNewPrivMessage(const QString &originating, const QString &user, const QString &message) +{ + IRCContact *from = findUser(originating); + IRCUserContact *to = findUser(user); + emit privateMessage(from, to, message); +} + +void IRCContactManager::unregister(Kopete::Contact *contact) +{ + unregisterChannel(contact, true); + unregisterUser(contact, true); +} + +QValueList<IRCChannelContact*> IRCContactManager::findChannelsByMember( IRCUserContact *contact ) +{ + QValueList<IRCChannelContact*> retVal; + for( QDictIterator<IRCChannelContact> it(m_channels); it.current(); ++it ) + { + if( it.current()->manager(Kopete::Contact::CannotCreate) ) + { + if( contact == m_mySelf ) + retVal.push_back( it.current() ); + else + { + bool c = true; + + Kopete::ContactPtrList members = it.current()->manager()->members(); + for( QPtrListIterator<Kopete::Contact> it2( members ); c && it2.current(); ++it2 ) + { + if( it2.current() == contact ) + { + retVal.push_back( it.current() ); + c = false; + } + } + } + } + } + + return retVal; +} + +IRCChannelContact *IRCContactManager::findChannel(const QString &name, Kopete::MetaContact *m) +{ + IRCChannelContact *channel = m_channels[ name ]; + + if ( !channel ) + { + if( !m ) + { + m = new Kopete::MetaContact(); + m->setTemporary( true ); + } + + channel = new IRCChannelContact(this, name, m); + m_channels.insert( name, channel ); + QObject::connect(channel, SIGNAL(contactDestroyed(Kopete::Contact *)), + this, SLOT(unregister(Kopete::Contact *))); + } + + return channel; +} + +IRCChannelContact *IRCContactManager::existChannel( const QString &channel ) const +{ + return m_channels[ channel ]; +} + +void IRCContactManager::unregisterChannel(Kopete::Contact *contact, bool force ) +{ + IRCChannelContact *channel = (IRCChannelContact*)contact; + if( force || ( + channel!=0 && + !channel->isChatting() && + channel->metaContact()->isTemporary() ) ) + { + m_channels.remove( channel->nickName() ); + } +} + +IRCUserContact *IRCContactManager::findUser(const QString &name, Kopete::MetaContact *m) +{ + IRCUserContact *user = m_users[name.section('!', 0, 0)]; + + if ( !user ) + { + if( !m ) + { + m = new Kopete::MetaContact(); + m->setTemporary( true ); + } + + user = new IRCUserContact(this, name, m); + m_users.insert( name, user ); + QObject::connect(user, SIGNAL(contactDestroyed(Kopete::Contact *)), + this, SLOT(unregister(Kopete::Contact *))); + } + + return user; +} + +IRCUserContact *IRCContactManager::existUser( const QString &user ) const +{ + return m_users[user]; +} + +IRCContact *IRCContactManager::findContact( const QString &id, Kopete::MetaContact *m ) +{ + if( KIRC::Entity::isChannel(id) ) + return findChannel( id, m ); + else + return findUser( id, m ); +} + +IRCContact *IRCContactManager::existContact( const KIRC::Engine *engine, const QString &id ) +{ + QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( IRCProtocol::protocol() ); + QDictIterator<Kopete::Account> it(accounts); + for( ; it.current(); ++it ) + { + IRCAccount *account = (IRCAccount *)it.current(); + if( account && account->engine() == engine ) + return account->contactManager()->existContact(id); + } + return 0L; +} + +IRCContact *IRCContactManager::existContact( const QString &id ) const +{ + if( KIRC::Entity::isChannel(id) ) + return existChannel( id ); + else + return existUser( id ); +} + +void IRCContactManager::unregisterUser(Kopete::Contact *contact, bool force ) +{ + IRCUserContact *user = (IRCUserContact *)contact; + if( force || ( + user!=0 && + user!=mySelf() && + !user->isChatting() && + user->metaContact()->isTemporary() ) ) + { + m_users.remove( user->nickName() ); + } +} + +void IRCContactManager::slotContactAdded( Kopete::MetaContact *contact ) +{ + for( QPtrListIterator<Kopete::Contact> it( contact->contacts() ); it.current(); ++it ) + { + if( it.current()->account() == m_account ) + { + addToNotifyList( static_cast<IRCContact*>( it.current() )->nickName() ); + } + } +} + +void IRCContactManager::addToNotifyList(const QString &nick) +{ + if (!m_NotifyList.contains(nick.lower())) + { + m_NotifyList.append(nick); + checkOnlineNotifyList(); + } +} + +void IRCContactManager::removeFromNotifyList(const QString &nick) +{ + if (m_NotifyList.contains(nick.lower())) + m_NotifyList.remove(nick.lower()); +} + +void IRCContactManager::checkOnlineNotifyList() +{ + if( m_account->engine()->isConnected() ) + { + isonRecieved = false; + m_account->engine()->ison( m_NotifyList ); + //QTimer::singleShot( socketTimeout, this, SLOT( slotIsonTimeout() ) ); + } +} + +void IRCContactManager::slotIsonRecieved() +{ + isonRecieved = true; +} + +void IRCContactManager::slotIsonTimeout() +{ + if( !isonRecieved ) + m_account->engine()->quit("", true); +} + +#include "irccontactmanager.moc" diff --git a/kopete/protocols/irc/irccontactmanager.h b/kopete/protocols/irc/irccontactmanager.h new file mode 100644 index 00000000..4a8ae05f --- /dev/null +++ b/kopete/protocols/irc/irccontactmanager.h @@ -0,0 +1,117 @@ +/* + irccontactmanager.h - Manager of IRC Contacts + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCCONTACTMANAGER_H +#define IRCCONTACTMANAGER_H + +#include <qdict.h> +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> + +class IRCContact; +class IRCAccount; + +class IRCServerContact; +class IRCChannelContact; +class IRCUserContact; + +namespace KIRC +{ +class Engine; +} + +namespace Kopete +{ +class Contact; +class MetaContact; +} + +class KopeteView; + +class QTimer; + +/** + * @author Michel Hermier <michel.hermier@wanadoo.fr> + * + * This class is the repository for all the reference of the @ref IRCContact childs. + * It manage the life cycle of all the @ref IRCServerContact, @ref IRCChannelContact and @ref IRCUserContact objects for the given account. + */ +class IRCContactManager + : public QObject +{ + Q_OBJECT + + public: + IRCContactManager(const QString &nickName, IRCAccount *account, const char *name=0); + + IRCAccount *account() const { return m_account; } + + IRCServerContact *myServer() const { return m_myServer; } + IRCUserContact *mySelf() const { return m_mySelf; } + + IRCChannelContact *findChannel(const QString &channel, Kopete::MetaContact *m=0); + IRCChannelContact *existChannel(const QString &channel) const; + + IRCUserContact *findUser(const QString &nick, Kopete::MetaContact *m=0); + IRCUserContact *existUser(const QString &nick) const; + + IRCContact *findContact(const QString &nick, Kopete::MetaContact *m=0); + IRCContact *existContact( const QString &id ) const; + + QValueList<IRCChannelContact*> findChannelsByMember( IRCUserContact *contact ); + + static IRCContact *existContact(const KIRC::Engine *engine, const QString &nick); + + public slots: + void unregister(Kopete::Contact *contact); + void unregisterUser(Kopete::Contact *contact, bool force = false ); + void unregisterChannel(Kopete::Contact *contact, bool force = false ); + + void addToNotifyList(const QString &nick); + void removeFromNotifyList(const QString &nick); + void checkOnlineNotifyList(); + + signals: + void privateMessage(IRCContact *from, IRCContact *to, const QString &message); + + private slots: + void slotNewMessage(const QString &originating, const QString &channel, const QString &message); + void slotNewPrivMessage(const QString &originating, const QString &, const QString &message); + void slotIsonRecieved(); + void slotIsonTimeout(); + void slotNewNickChange(const QString &oldnick, const QString &newnick); + void slotContactAdded( Kopete::MetaContact *contact ); + + private: + QDict<IRCChannelContact> m_channels; + QDict<IRCUserContact> m_users; + + IRCAccount *m_account; + IRCServerContact *m_myServer; + IRCUserContact *m_mySelf; + + QStringList m_NotifyList; + QTimer *m_NotifyTimer; + bool isonRecieved; + int socketTimeout; + + static const QRegExp isChannel; +}; + +#endif + diff --git a/kopete/protocols/irc/ircguiclient.cpp b/kopete/protocols/irc/ircguiclient.cpp new file mode 100644 index 00000000..b4c36973 --- /dev/null +++ b/kopete/protocols/irc/ircguiclient.cpp @@ -0,0 +1,100 @@ +/* + ircguiclient.cpp + + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Kopete (c) 2003 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 <klocale.h> + +#include <kdeversion.h> +#if KDE_IS_VERSION( 3, 1, 90 ) + #include <kactioncollection.h> +#else +// ------------------------------------------------------------ +// TODO: UGLY HACK, remove when we drop KDE 3.1 compatibility +#ifdef KDE_NO_COMPAT +#undef KDE_NO_COMPAT +#include <kaction.h> +#define KDE_NO_COMPAT 1 +#endif +// ------------------------------------------------------------ +#endif + +#include <qptrlist.h> +#include <kdebug.h> +#include <qdom.h> + +#include "kopetechatsession.h" +#include "kcodecaction.h" +#include "ircguiclient.h" +#include "ircaccount.h" +#include "irccontact.h" + +IRCGUIClient::IRCGUIClient( Kopete::ChatSession *parent ) : QObject(parent) , KXMLGUIClient(parent) +{ + Kopete::ContactPtrList members = parent->members(); + if( members.count() > 0 ) + { + m_user = static_cast<IRCContact*>( members.first() ); + + /*** + FIXME: Why doesn't this work???? Have to use DOM hack below now... + + setXMLFile("ircchatui.rc"); + + unplugActionList( "irccontactactionlist" ); + QPtrList<KAction> *actions = m_user->customContextMenuActions( parent ); + plugActionList( "irccontactactionlist", *actions ); + delete actions; + */ + + setXMLFile("ircchatui.rc"); + + QDomDocument doc = domDocument(); + QDomNode menu = doc.documentElement().firstChild().firstChild(); + QPtrList<KAction> *actions = m_user->customContextMenuActions( parent ); + if( actions ) + { + for( KAction *a = actions->first(); a; a = actions->next() ) + { + actionCollection()->insert( a ); + QDomElement newNode = doc.createElement( "Action" ); + newNode.setAttribute( "name", a->name() ); + menu.appendChild( newNode ); + } + } + else + { + kdDebug(14120) << k_funcinfo << "Actions == 0" << endl; + } + + delete actions; + + setDOMDocument( doc ); + } + else + { + kdDebug(14120) << k_funcinfo << "Members == 0" << endl; + } +} + +IRCGUIClient::~IRCGUIClient() +{ +} + +void IRCGUIClient::slotSelectCodec( const QTextCodec *codec ) +{ + m_user->setCodec( codec ); +} + +#include "ircguiclient.moc" diff --git a/kopete/protocols/irc/ircguiclient.h b/kopete/protocols/irc/ircguiclient.h new file mode 100644 index 00000000..b81aa632 --- /dev/null +++ b/kopete/protocols/irc/ircguiclient.h @@ -0,0 +1,42 @@ +/* + ircguiclient.h + + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ +#ifndef IRCGUICLIENT_H +#define IRCGUICLIENT_H + +#include <qobject.h> +#include <kxmlguiclient.h> + +namespace Kopete { class ChatSession; } +class IRCContact; + +/** + *@author Jason Keirstead + */ +class IRCGUIClient : public QObject , public KXMLGUIClient +{ + Q_OBJECT + public: + IRCGUIClient( Kopete::ChatSession *parent = 0 ); + ~IRCGUIClient(); + + private slots: + void slotSelectCodec( const QTextCodec *codec ); + + private: + IRCContact *m_user; +}; + +#endif diff --git a/kopete/protocols/irc/ircnetworks.xml b/kopete/protocols/irc/ircnetworks.xml new file mode 100644 index 00000000..c743e9e0 --- /dev/null +++ b/kopete/protocols/irc/ircnetworks.xml @@ -0,0 +1,1463 @@ +<!DOCTYPE irc-networks> +<networks> + <network> + <name>AnyNet</name> + <description>AnyNet</description> + <servers> + <server> + <host>irc.anynet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>IRCNet</name> + <description>IRCNet</description> + <servers> + <server> + <host>eu.ircnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ircd.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>au.ircnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.stealth.net</host> + <port>6660</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>KewlNet</name> + <description>KewlNet</description> + <servers> + <server> + <host>irc.kewl.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>la.defense.fr.eu.kewl.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>nanterre.fr.eu.kewl.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>TrekLink</name> + <description>TrekLink</description> + <servers> + <server> + <host>neutron.treklink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.treklink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>SlashNET</name> + <description>SlashNET</description> + <servers> + <server> + <host>irc.slashnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>area51.slashnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>moo.slashnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>radon.slashnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>devnull.slashnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>NeverNET</name> + <description>NeverNET</description> + <servers> + <server> + <host>irc.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>imagine.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>dimension.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>universe.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>wayland.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>forte.nevernet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>CoolChat</name> + <description>CoolChat</description> + <servers> + <server> + <host>irc.coolchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>unix.coolchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>south.coolchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>toronto.coolchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>UnderNet</name> + <description>UnderNet</description> + <servers> + <server> + <host>us.undernet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>eu.undernet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>MagicStar</name> + <description>MagicStar</description> + <servers> + <server> + <host>irc.magicstar.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>PTNet, UNI</name> + <description>PTNet, UNI</description> + <servers> + <server> + <host>irc.PTNet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>rccn.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uevora.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>umoderna.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ist.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>aaum.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uc.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ualg.ptnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>madinfo.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>isep.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ua.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ipg.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>isec.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>utad.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>iscte.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ubi.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>FDFNet</name> + <description>FDFNet</description> + <servers> + <server> + <host>irc.fdfnet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.eu.fdfnet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>FEFNet</name> + <description>FEFNet</description> + <servers> + <server> + <host>irc.fef.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.villagenet.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ggn.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.vendetta.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>KrushNet.Org</name> + <description>KrushNet.Org</description> + <servers> + <server> + <host>Jeffersonville.IN.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Auckland.NZ.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Hastings.NZ.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Seattle-R.WA.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Minneapolis.MN.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Cullowhee.NC.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Asheville-R.NC.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>San-Antonio.TX.US.KrushNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AfterNET</name> + <description>AfterNET</description> + <servers> + <server> + <host>irc.afternet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ic5.eu.afternet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>baltimore.md.us.afternet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>boston.afternet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>DragonLynk</name> + <description>DragonLynk</description> + <servers> + <server> + <host>irc.dragonlynk.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>EFNet</name> + <description>EFNet</description> + <servers> + <server> + <host>us.rr.efnet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.arcti.ca</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>eu.rr.efnet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>au.rr.efnet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.efnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.light.se</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.stanford.edu</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.solidstreaming.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>IrcLink</name> + <description>IrcLink</description> + <servers> + <server> + <host>irc.irclink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Alesund.no.eu.irclink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Oslo.no.eu.irclink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>frogn.no.eu.irclink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>tonsberg.no.eu.irclink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AstroLINK.Org</name> + <description>AstroLINK.Org</description> + <servers> + <server> + <host>irc.astrolink.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>GalaxyNet</name> + <description>GalaxyNet</description> + <servers> + <server> + <host>sprynet.us.galaxynet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>atlanta.ga.us.galaxynet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.galaxynet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>SceneNet</name> + <description>SceneNet</description> + <servers> + <server> + <host>irc.scene.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.eu.scene.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.us.scene.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>EUIrc</name> + <description>EUIrc</description> + <servers> + <server> + <host>irc.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ham.de.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ber.de.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ffm.de.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.bre.de.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.hes.de.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.vie.at.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.inn.at.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.bas.ch.euirc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>RebelChat</name> + <description>RebelChat</description> + <servers> + <server> + <host>irc.rebelchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>interquad.rebelchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>rebel.rebelchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>bigcove.rebelchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>ARCNet</name> + <description>ARCNet</description> + <servers> + <server> + <host>se1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us2.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us3.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ca1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>de1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>de3.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ch1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>be1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>nl3.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk2.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk3.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>fr1.arcnet.vapor.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>Librenet</name> + <description>Librenet</description> + <servers> + <server> + <host>irc.librenet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>famipow.fr.librenet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ielf.fr.librenet.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>SubCultNet</name> + <description>SubCultNet</description> + <servers> + <server> + <host>irc.subcult.ch</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.phuncrew.ch</host> + <port>6668</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.mgz.ch</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>freenode</name> + <description>freenode, a service by Peer-directed Projects Center</description> + <servers> + <server> + <host>irc.kde.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>chat.freenode.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>chat.us.freenode.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>chat.eu.freenode.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>chat.au.freenode.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>OFTC</name> + <description>The Open and Free Technology Community</description> + <servers> + <server> + <host>irc.oftc.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ircs.oftc.net</host> + <port>9999</port> + <useSSL>true</useSSL> + </server> + </servers> + </network> + <network> + <name>XWorld</name> + <description>XWorld</description> + <servers> + <server> + <host>Buffalo.NY.US.XWorld.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Minneapolis.MN.US.Xworld.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>PalmSprings.CA.US.XWorld.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Quebec.QC.CA.XWorld.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Rochester.NY.US.XWorld.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Bayern.DE.EU.XWorld.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Chicago.IL.US.XWorld.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>ChatNet</name> + <description>ChatNet</description> + <servers> + <server> + <host>US.ChatNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>EU.ChatNet.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>Neohorizon</name> + <description>Neohorizon</description> + <servers> + <server> + <host>irc.nhn.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>QChat.net</name> + <description>QChat.net</description> + <servers> + <server> + <host>irc.qchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>StarChat</name> + <description>StarChat</description> + <servers> + <server> + <host>irc.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>galatea.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>stargate.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>powerzone.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>utopia.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>cairns.starchat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>Infinity-IRC.org</name> + <description>Infinity-IRC.org</description> + <servers> + <server> + <host>Atlanta.GA.US.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Babylon.NY.US.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Dewspeak.TX.US.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Sunshine.Ca.US.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>MNC.MD.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>IRC.Infinity-IRC.Org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>HabberNet</name> + <description>HabberNet</description> + <servers> + <server> + <host>irc.habber.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>Mellorien</name> + <description>Mellorien</description> + <servers> + <server> + <host>Irc.mellorien.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.mellorien.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>eu.mellorien.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>DwarfStarNet</name> + <description>DwarfStarNet</description> + <servers> + <server> + <host>IRC.dwarfstar.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>US.dwarfstar.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>EU.dwarfstar.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>AU.dwarfstar.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>ChatJunkiesNet</name> + <description>ChatJunkiesNet</description> + <servers> + <server> + <host>irc.xchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.xchat.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>OtherNet</name> + <description>OtherNet</description> + <servers> + <server> + <host>irc.othernet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AxeNet</name> + <description>AxeNet</description> + <servers> + <server> + <host>irc.axenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>angel.axenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>energy.axenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>python.axenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>unsecurity.org</name> + <description>unsecurity.org</description> + <servers> + <server> + <host>irc.unsecurity.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>wc.unsecurity.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>thegift.unsecurity.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>sysgate.unsecurity.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>DALNet</name> + <description>DALNet</description> + <servers> + <server> + <host>irc.dal.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.eu.dal.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AustNet</name> + <description>AustNet</description> + <servers> + <server> + <host>us.austnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ca.austnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>au.austnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>PTNet, ISP's</name> + <description>PTNet, ISP's</description> + <servers> + <server> + <host>irc.PTNet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>rccn.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>EUnet.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>madinfo.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>netc2.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>netc1.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>teleweb.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>netway.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>telepac1.ptnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>services.ptnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>esoterica.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ip-hub.ptnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>telepac1.ptnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>nortenet.PTnet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>NixHelpNet</name> + <description>NixHelpNet</description> + <servers> + <server> + <host>irc.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk2.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uk3.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>nl.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>ca.ld.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.co.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.ca.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>us.pa.nixhelp.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>Gamma Force</name> + <description>Gamma Force</description> + <servers> + <server> + <host>irc.gammaforce.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>sphinx.or.us.gammaforce.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>monolith.ok.us.gammaforce.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>PTlink</name> + <description>PTlink</description> + <servers> + <server> + <host>irc.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>dark.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>uc.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>kungfoo.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>matrix.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>illusion.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>Cibercultura.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>aaia.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>gaesi.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>BuBix.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>montijo.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>queima.PTlink.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>OzNet</name> + <description>OzNet</description> + <servers> + <server> + <host>sydney.oz.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>melbourne.oz.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>FoxChat</name> + <description>FoxChat</description> + <servers> + <server> + <host>irc.FoxChat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ac6.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>beastie.ac6.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>wild.FoxChat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>roadkill.FoxChat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>slick.FoxChat.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AzzurraNet</name> + <description>AzzurraNet</description> + <servers> + <server> + <host>irc.bitchx.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.jnet.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.net36.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.noflyzone.net</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.swappoint.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.azzurra.com</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.leonet.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.libero.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.estranet.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.filmaker.it</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> + <network> + <name>AbleNET</name> + <description>AbleNET</description> + <servers> + <server> + <host>california.ablenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>amazon.ablenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>agora.ablenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>extreme.ablenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + <server> + <host>irc.ablenet.org</host> + <port>6667</port> + <useSSL>false</useSSL> + </server> + </servers> + </network> +</networks> diff --git a/kopete/protocols/irc/ircprotocol.cpp b/kopete/protocols/irc/ircprotocol.cpp new file mode 100644 index 00000000..176c74d7 --- /dev/null +++ b/kopete/protocols/irc/ircprotocol.cpp @@ -0,0 +1,1241 @@ +/* + 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: diff --git a/kopete/protocols/irc/ircprotocol.h b/kopete/protocols/irc/ircprotocol.h new file mode 100644 index 00000000..2a1700e5 --- /dev/null +++ b/kopete/protocols/irc/ircprotocol.h @@ -0,0 +1,228 @@ +/* + ircprotocol.h - 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCPROTOCOL_H +#define IRCPROTOCOL_H + +#include "kopeteonlinestatus.h" +#include "kopeteprotocol.h" +#include "kopetecontactproperty.h" +#include "kopetemimetypehandler.h" + +#include <dom/dom_node.h> +#include <qdict.h> + +#define m_protocol (IRCProtocol::protocol()) + +namespace Kopete +{ +class Account; +class MetaContact; +} + +class AddContactPage; + +class EditAccountWidget; +class IRCAccount; + +class QStringList; +class QWidget; +class KopeteView; + +class IRCNetwork; +class IRCHost; +class NetworkConfig; + +class IRCProtocolHandler : public Kopete::MimeTypeHandler +{ + public: + + IRCProtocolHandler(); + + void handleURL( const KURL &url ) const; +}; + +static const QString CHAT_VIEW( QString::fromLatin1("kopete_chatwindow") ); + +/** + * @author Nick Betcher <nbetcher@kde.org> + */ +class IRCProtocol : public Kopete::Protocol +{ + Q_OBJECT + +public: + enum IRCStatus + { + Offline = 1, //! An offline user. + Connecting = 2, //! User that is connecting. + Away = 4, //! User that is away. May be regular user, voiced user or (server) operator. + Online = 8, //! This user is online. + Voiced = 16, //! This user is voiced. + Operator = 32, //! This user is a channel operator. + ServerOperator = 1024, //! This user is a server operator. + OfflineChannel = 4096, //! This channel is offline. + OnlineChannel = 8192, //! This channel is online. + OfflineServer = 16384, //! This server is offline. + OnlineServer = 32768 //! This server is online. + }; + + IRCProtocol( QObject *parent, const char *name, const QStringList &args ); + ~IRCProtocol(); + + /** Kopete::Protocol reimplementation */ + virtual AddContactPage *createAddContactWidget(QWidget *parent, Kopete::Account *account); + + /** + * Deserialize contact data + */ + virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact, + const QMap<QString, QString> &serializedData, const QMap<QString, QString> &addressBookData ); + + virtual KopeteEditAccountWidget* createEditAccountWidget(Kopete::Account *account, QWidget *parent); + + virtual Kopete::Account* createNewAccount(const QString &accountId); + + virtual QPtrList<KAction> *customChatWindowPopupActions( const Kopete::Message &, DOM::Node & ); + + static IRCProtocol *protocol(); + + /** + * Maps the given IRC status to Kopete::OnlineStatus. + */ + const Kopete::OnlineStatus statusLookup( IRCStatus status ) const; + + const Kopete::OnlineStatus m_ServerStatusOnline; + const Kopete::OnlineStatus m_ServerStatusOffline; + + const Kopete::OnlineStatus m_ChannelStatusOnline; + const Kopete::OnlineStatus m_ChannelStatusOffline; + + const Kopete::OnlineStatus m_UserStatusOpVoice; + const Kopete::OnlineStatus m_UserStatusOpVoiceAway; + const Kopete::OnlineStatus m_UserStatusOp; + const Kopete::OnlineStatus m_UserStatusOpAway; + const Kopete::OnlineStatus m_UserStatusVoice; + const Kopete::OnlineStatus m_UserStatusVoiceAway; + const Kopete::OnlineStatus m_UserStatusOnline; + const Kopete::OnlineStatus m_UserStatusAway; + const Kopete::OnlineStatus m_UserStatusConnecting; + const Kopete::OnlineStatus m_UserStatusOffline; + + const Kopete::OnlineStatus m_StatusUnknown; + + // irc channnel-contact properties + const Kopete::ContactPropertyTmpl propChannelTopic; + const Kopete::ContactPropertyTmpl propChannelMembers; + const Kopete::ContactPropertyTmpl propHomepage; + + // irc user-contact properties + const Kopete::ContactPropertyTmpl propLastSeen; + const Kopete::ContactPropertyTmpl propUserInfo; + const Kopete::ContactPropertyTmpl propServer; + const Kopete::ContactPropertyTmpl propChannels; + const Kopete::ContactPropertyTmpl propHops; + const Kopete::ContactPropertyTmpl propFullName; + const Kopete::ContactPropertyTmpl propIsIdentified; + + bool commandInProgress(){ return m_commandInProgress; } + void setCommandInProgress( bool ip ) { m_commandInProgress = ip; } + + QDict<IRCNetwork> &networks(){ return m_networks; } + void addNetwork( IRCNetwork *network ); + + void editNetworks( const QString &networkName = QString::null ); + +signals: + void networkConfigUpdated( const QString &selectedNetwork ); + +private slots: + // FIXME: All the code for managing the networks list should be in another class - Will + void slotUpdateNetworkConfig(); + void slotUpdateNetworkHostConfig(); + void slotMoveServerUp(); + void slotMoveServerDown(); + void slotSaveNetworkConfig(); + void slotReadNetworks(); + void slotDeleteNetwork(); + void slotDeleteHost(); + void slotNewNetwork(); + void slotRenameNetwork(); + void slotNewHost(); + void slotHostPortChanged( int value ); + // end of network list specific code + + void slotMessageFilter( Kopete::Message &msg ); + + void slotRawCommand( const QString &args, Kopete::ChatSession *manager ); + void slotQuoteCommand( const QString &args, Kopete::ChatSession *manager ); + void slotCtcpCommand( const QString &args, Kopete::ChatSession *manager ); + void slotPingCommand( const QString &args, Kopete::ChatSession *manager ); + + void slotMotdCommand( const QString &args, Kopete::ChatSession *manager); + void slotListCommand( const QString &args, Kopete::ChatSession *manager); + void slotTopicCommand( const QString &args, Kopete::ChatSession *manager); + void slotJoinCommand( const QString &args, Kopete::ChatSession *manager); + void slotNickCommand( const QString &args, Kopete::ChatSession *manager); + void slotWhoisCommand( const QString &args, Kopete::ChatSession *manager); + void slotWhoWasCommand( const QString &args, Kopete::ChatSession *manager); + void slotWhoCommand( const QString &args, Kopete::ChatSession *manager); + void slotMeCommand( const QString &args, Kopete::ChatSession *manager); + void slotAllMeCommand( const QString &args, Kopete::ChatSession *manager); + void slotModeCommand( const QString &args, Kopete::ChatSession *manager); + void slotQueryCommand( const QString &args, Kopete::ChatSession *manager); + + void slotKickCommand( const QString &args, Kopete::ChatSession *manager); + void slotBanCommand( const QString &args, Kopete::ChatSession *manager); + void slotOpCommand( const QString &args, Kopete::ChatSession *manager); + void slotDeopCommand( const QString &args, Kopete::ChatSession *manager); + void slotVoiceCommand( const QString &args, Kopete::ChatSession *manager); + void slotDevoiceCommand( const QString &args, Kopete::ChatSession *manager); + void slotQuitCommand( const QString &args, Kopete::ChatSession *manager); + void slotPartCommand( const QString &args, Kopete::ChatSession *manager); + void slotInviteCommand( const QString &args, Kopete::ChatSession *manager); + + void slotViewCreated( KopeteView * ); + +private: + static IRCProtocol *s_protocol; + + void simpleModeChange( const QString &, Kopete::ChatSession *, const QString &mode ); + + // FIXME: All the code for managing the networks list should be in another class - Will + void storeCurrentNetwork(); + void storeCurrentHost(); + + NetworkConfig *netConf; + QString m_uiCurrentNetworkSelection; + QString m_uiCurrentHostSelection; + // end of network list specific code + + DOM::Node activeNode; + IRCAccount *activeAccount; + + bool m_commandInProgress; + + QDict<IRCNetwork> m_networks; + QDict<IRCHost> m_hosts; + IRCProtocolHandler *m_protocolHandler; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ircservercontact.cpp b/kopete/protocols/irc/ircservercontact.cpp new file mode 100644 index 00000000..3ca21643 --- /dev/null +++ b/kopete/protocols/irc/ircservercontact.cpp @@ -0,0 +1,220 @@ +/* + ircservercontact.cpp - IRC Server Contact + + Copyright (c) 2003 by Michel Hermier <mhermier@kde.org> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002-2003 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 "ircusercontact.h" +#include "ircservercontact.h" +#include "ircaccount.h" +#include "ircprotocol.h" + +#include "kopetechatsessionmanager.h" +#include "kopeteview.h" + +#include <kaction.h> +#include <kdebug.h> +#include <klocale.h> + +#include <qtimer.h> + +IRCServerContact::IRCServerContact(IRCContactManager *contactManager, const QString &servername, Kopete::MetaContact *m) + : IRCContact(contactManager, servername, m, "irc_server") +{ + KIRC::Engine *engine = kircEngine(); + + QObject::connect(engine, SIGNAL(internalError(KIRC::Engine::Error, KIRC::Message &)), + this, SLOT(engineInternalError(KIRC::Engine::Error, KIRC::Message &))); +/* + //FIXME: Have some kind of a debug option for raw input/ouput display?? + QObject::connect(engine, SIGNAL(sentMessage(KIRC::Message &)), + this, SLOT(engineSentMessage(KIRC::Message &))); + QObject::connect(engine, SIGNAL(receivedMessage(KIRC::Message &)), + this, SLOT(engineReceivedMessage(KIRC::Message &))); +*/ + + QObject::connect(engine, SIGNAL(incomingNotice(const QString &, const QString &)), + this, SLOT(slotIncomingNotice(const QString &, const QString &))); + + QObject::connect(engine, SIGNAL(incomingCannotSendToChannel(const QString &, const QString &)), + this, SLOT(slotCannotSendToChannel(const QString &, const QString &))); + + QObject::connect(engine, SIGNAL(incomingUnknown(const QString &)), + this, SLOT(slotIncomingUnknown(const QString &))); + + QObject::connect(engine, SIGNAL(incomingConnectString(const QString &)), + this, SLOT(slotIncomingConnect(const QString &))); + + QObject::connect(engine, SIGNAL(incomingMotd(const QString &)), + this, SLOT(slotIncomingMotd(const QString &))); + + QObject::connect(Kopete::ChatSessionManager::self(), SIGNAL(viewCreated(KopeteView*)), + this, SLOT(slotViewCreated(KopeteView*)) ); + + updateStatus(); +} + +void IRCServerContact::updateStatus() +{ + KIRC::Engine::Status status = kircEngine()->status(); + switch( status ) + { + case KIRC::Engine::Idle: + case KIRC::Engine::Connecting: + if( m_chatSession ) + m_chatSession->setDisplayName( caption() ); + setOnlineStatus( m_protocol->m_ServerStatusOffline ); + break; + + case KIRC::Engine::Authentifying: + case KIRC::Engine::Connected: + case KIRC::Engine::Closing: + // should make some extra check here + setOnlineStatus( m_protocol->m_ServerStatusOnline ); + break; + + default: + setOnlineStatus( m_protocol->m_StatusUnknown ); + } +} + +const QString IRCServerContact::caption() const +{ + return i18n("%1 @ %2").arg(ircAccount()->mySelf()->nickName() ).arg( + kircEngine()->currentHost().isEmpty() ? ircAccount()->networkName() : kircEngine()->currentHost() + ); +} + +void IRCServerContact::engineInternalError(KIRC::Engine::Error engineError, KIRC::Message &ircmsg) +{ + QString error; + switch (engineError) + { + case KIRC::Engine::ParsingFailed: + error = i18n("KIRC Error - Parse error: "); + break; + case KIRC::Engine::UnknownCommand: + error = i18n("KIRC Error - Unknown command: "); + break; + case KIRC::Engine::UnknownNumericReply: + error = i18n("KIRC Error - Unknown numeric reply: "); + break; + case KIRC::Engine::InvalidNumberOfArguments: + error = i18n("KIRC Error - Invalid number of arguments: "); + break; + case KIRC::Engine::MethodFailed: + error = i18n("KIRC Error - Method failed: "); + break; + default: + error = i18n("KIRC Error - Unknown error: "); + } + + ircAccount()->appendMessage(error + QString(ircmsg.raw()), IRCAccount::ErrorReply); +} + +void IRCServerContact::slotSendMsg(Kopete::Message &, Kopete::ChatSession *manager) +{ + manager->messageSucceeded(); + Kopete::Message msg( manager->myself(), manager->members(), + i18n("You can not talk to the server, you can only issue commands here. Type /help for supported commands."), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW); + manager->appendMessage(msg); +} + +void IRCServerContact::appendMessage( const QString &message ) +{ + Kopete::ContactPtrList members; + members.append( this ); + Kopete::Message msg( this, members, message, Kopete::Message::Internal, + Kopete::Message::RichText, CHAT_VIEW ); + appendMessage(msg); +} + +void IRCServerContact::slotIncomingNotice( const QString &orig, const QString ¬ice ) +{ + if (orig.isEmpty()) { + // Prefix missing. + // NOTICE AUTH :*** Checking Ident + + ircAccount()->appendMessage(i18n("NOTICE from %1: %2").arg(kircEngine()->currentHost(), notice), + IRCAccount::NoticeReply); + + } else { + // :Global!service@rizon.net NOTICE foobar :[Logon News - Oct 12 2005] Due to growing problems ... + // :somenick!~fooobar@somehostname.fi NOTICE foobar :hello + + if (orig.contains('!')) { + ircAccount()->appendMessage(i18n("NOTICE from %1 (%2): %3").arg( + orig.section('!', 0, 0), + orig.section('!', 1, 1), + notice), + IRCAccount::NoticeReply); + } else { + ircAccount()->appendMessage(i18n("NOTICE from %1: %2").arg( + orig, notice), IRCAccount::NoticeReply); + } + } +} + +void IRCServerContact::slotIncomingUnknown(const QString &message) +{ + ircAccount()->appendMessage(message, IRCAccount::UnknownReply); +} + +void IRCServerContact::slotIncomingConnect(const QString &message) +{ + ircAccount()->appendMessage(message, IRCAccount::ConnectReply); +} + +void IRCServerContact::slotIncomingMotd(const QString &message) +{ + ircAccount()->appendMessage(message, IRCAccount::InfoReply); +} + +void IRCServerContact::slotCannotSendToChannel(const QString &channel, const QString &message) +{ + ircAccount()->appendMessage(QString::fromLatin1("%1: %2").arg(channel).arg(message), IRCAccount::ErrorReply); +} + +void IRCServerContact::appendMessage(Kopete::Message &msg) +{ + msg.setImportance( Kopete::Message::Low ); //to don't distrub the user + + if (m_chatSession && m_chatSession->view(false)) + m_chatSession->appendMessage(msg); +/* +// disable the buffering for now: cause a memleak since we don't made it a *fixed size fifo* + else + mMsgBuffer.append( msg ); +*/ +} + +void IRCServerContact::slotDumpMessages() +{ + if (!mMsgBuffer.isEmpty()) + { + manager()->appendMessage( mMsgBuffer.front() ); + mMsgBuffer.pop_front(); + QTimer::singleShot( 0, this, SLOT( slotDumpMessages() ) ); + } +} + +void IRCServerContact::slotViewCreated( KopeteView *v ) +{ + kdDebug(14121) << k_funcinfo << "Created: " << v << ", mgr: " << v->msgManager() << ", Mine: " << m_chatSession << endl; + if (m_chatSession && v->msgManager() == m_chatSession) + QTimer::singleShot(500, this, SLOT(slotDumpMessages())); +} + +#include "ircservercontact.moc" diff --git a/kopete/protocols/irc/ircservercontact.h b/kopete/protocols/irc/ircservercontact.h new file mode 100644 index 00000000..1ca1475b --- /dev/null +++ b/kopete/protocols/irc/ircservercontact.h @@ -0,0 +1,80 @@ +/* + ircservercontact.h - IRC User Contact + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCSERVERCONTACT_H +#define IRCSERVERCONTACT_H + +#include "irccontact.h" + +#include "kircengine.h" + +#include "kopetechatsessionmanager.h" + +#include <qvaluelist.h> +#include <qstringlist.h> + +class KActionCollection; +class KAction; +class KActionMenu; +class KopeteView; + +class IRCContactManager; +class IRCChannelContact; + +/** + * @author Michel Hermier <michel.hermier@wanadoo.fr> + * + * This class is the @ref Kopete::Contact object representing IRC Servers. + * It is derrived from @ref IRCContact where much of its functionality is shared with @ref IRCChannelContact and @ref IRCUserContact. + */ +class IRCServerContact + : public IRCContact +{ + Q_OBJECT + + public: + // This class provides a Kopete::Contact for each server of a given IRC connection. + IRCServerContact(IRCContactManager *, const QString &servername, Kopete::MetaContact *mc); + + virtual const QString caption() const; + + virtual void appendMessage(Kopete::Message &); + void appendMessage( const QString &message ); + + protected slots: + void engineInternalError(KIRC::Engine::Error error, KIRC::Message &ircmsg); + virtual void slotSendMsg(Kopete::Message &message, Kopete::ChatSession *); + + private slots: + virtual void updateStatus(); + void slotViewCreated( KopeteView* ); + void slotDumpMessages(); + + void slotIncomingUnknown( const QString &message ); + void slotIncomingConnect( const QString &message ); + void slotIncomingMotd( const QString &motd ); + void slotIncomingNotice( const QString &orig, const QString ¬ice ); + void slotCannotSendToChannel( const QString &channel, const QString &msg ); + + private: + QValueList<Kopete::Message> mMsgBuffer; +}; + +#endif + +// vim: set noet ts=4 sts=4 tw=4: + diff --git a/kopete/protocols/irc/ircsignalhandler.cpp b/kopete/protocols/irc/ircsignalhandler.cpp new file mode 100644 index 00000000..5bfab0cc --- /dev/null +++ b/kopete/protocols/irc/ircsignalhandler.cpp @@ -0,0 +1,173 @@ + +/* + ircsignalhandler.cpp - Maps signals from the IRC engine to contacts + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2003 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 "ircusercontact.h" +#include "ircchannelcontact.h" +#include "ircsignalhandler.h" + +#include "kircengine.h" + +IRCSignalHandler::IRCSignalHandler(IRCContactManager *m) + : QObject(m), + manager(m) +{ + KIRC::Engine *m_engine = static_cast<IRCAccount*>( manager->mySelf()->account() )->engine(); + + //Channel Connections to ourself + QObject::connect(m_engine, SIGNAL(incomingNamesList(const QString &, const QStringList &)), + this, SLOT(slotNamesList(const QString &, const QStringList &))); + + QObject::connect(m_engine, SIGNAL(incomingEndOfNames(const QString &)), + this, SLOT(slotEndOfNames(const QString &))); + + QObject::connect(m_engine, SIGNAL(incomingTopicUser(const QString &, const QString &, const QDateTime &)), + this, SLOT(slotTopicUser(const QString&,const QString&,const QDateTime&))); + + //Channel String mappings + map<IRCChannelContact>( m, SIGNAL(incomingFailedChankey(const QString &)), + &IRCChannelContact::failedChankey ); + + map<IRCChannelContact>( m, SIGNAL(incomingFailedChanFull(const QString &)), + &IRCChannelContact::failedChanInvite ); + + map<IRCChannelContact>( m, SIGNAL(incomingFailedChanInvite(const QString &)), + &IRCChannelContact::failedChanInvite ); + + map<IRCChannelContact>( m, SIGNAL(incomingFailedChanBanned(const QString &)), + &IRCChannelContact::failedChanBanned ); + + mapSingle<IRCChannelContact>( m, SIGNAL(incomingJoinedChannel(const QString &, const QString &)), + &IRCChannelContact::userJoinedChannel ); + + mapSingle<IRCChannelContact>( m, SIGNAL(incomingExistingTopic(const QString &, const QString &)), + &IRCChannelContact::channelTopic ); + + mapSingle<IRCChannelContact>( m, SIGNAL(incomingChannelHomePage(const QString &, const QString &)), + &IRCChannelContact::channelHomePage ); + + mapDouble<IRCChannelContact>( m, + SIGNAL(incomingPartedChannel(const QString &, const QString &,const QString &)), + &IRCChannelContact::userPartedChannel ); + + mapDouble<IRCChannelContact>( m, + SIGNAL(incomingTopicChange(const QString &, const QString &,const QString &)), + &IRCChannelContact::topicChanged ); + + mapDouble<IRCChannelContact>( m, + SIGNAL(incomingChannelModeChange(const QString &, const QString &,const QString &)), + &IRCChannelContact::incomingModeChange ); + + mapDouble<IRCChannelContact>( m, + SIGNAL(incomingChannelMode(const QString &, const QString &,const QString &)), + &IRCChannelContact::incomingChannelMode ); + + mapTriple<IRCChannelContact>( m, + SIGNAL(incomingKick(const QString &, const QString &,const QString &,const QString &)), + &IRCChannelContact::userKicked ); + + //User connections to ourself + QObject::connect(m_engine, SIGNAL(incomingWhoIsIdle(const QString &, unsigned long )), + this, SLOT(slotNewWhoIsIdle(const QString &, unsigned long ))); + + QObject::connect(m_engine, SIGNAL(incomingWhoReply(const QString &, const QString &, const QString &, + const QString &, const QString &, bool, const QString &, uint, const QString & )), + this, SLOT( slotNewWhoReply(const QString &, const QString &, const QString &, const QString &, + const QString &, bool, const QString &, uint, const QString &))); + + //User signal mappings + map<IRCUserContact>( m, SIGNAL(incomingUserOnline( const QString & )), &IRCUserContact::userOnline ); + + map<IRCUserContact>( m, SIGNAL(incomingWhoIsOperator( const QString & )), &IRCUserContact::newWhoIsOperator ); + + map<IRCUserContact>( m, SIGNAL(incomingWhoIsIdentified( const QString & )), &IRCUserContact::newWhoIsIdentified ); + + map<IRCUserContact>( m, SIGNAL(incomingEndOfWhois( const QString & )), &IRCUserContact::whoIsComplete ); + + map<IRCUserContact>( m, SIGNAL(incomingEndOfWhoWas( const QString & )), &IRCUserContact::whoWasComplete ); + + mapSingle<IRCUserContact>( m, SIGNAL(incomingUserIsAway( const QString &, const QString & )), + &IRCUserContact::incomingUserIsAway ); + + mapSingle<IRCUserContact>( m, SIGNAL(incomingWhoIsChannels( const QString &, const QString & )), + &IRCUserContact::newWhoIsChannels ); + + mapDouble<IRCUserContact>( m, + SIGNAL(incomingWhoIsServer(const QString &, const QString &, const QString &)), + &IRCUserContact::newWhoIsServer ); + + mapDouble<IRCUserContact>( m, + SIGNAL(incomingPrivAction(const QString &, const QString &, const QString &)), + &IRCUserContact::newAction ); + + mapDouble<IRCChannelContact>( m, + SIGNAL(incomingAction(const QString &, const QString &, const QString &)), + &IRCChannelContact::newAction ); + + mapTriple<IRCUserContact>( m, + SIGNAL(incomingWhoIsUser(const QString &, const QString &, const QString &, const QString &)), + &IRCUserContact::newWhoIsUser ); + + mapTriple<IRCUserContact>( m, + SIGNAL(incomingWhoWasUser(const QString &, const QString &, const QString &, const QString &)), + &IRCUserContact::newWhoIsUser ); +} + +IRCSignalHandler::~IRCSignalHandler() +{ + //Delete our mapping pointers + for( QValueList<IRCSignalMappingBase*>::iterator it = mappings.begin(); it != mappings.end(); ++it ) + delete *it; +} + +void IRCSignalHandler::slotNamesList( const QString &chan, const QStringList &list ) +{ + IRCChannelContact *c = manager->existChannel( chan ); + if( c ) + c->namesList( list ); +} + +void IRCSignalHandler::slotEndOfNames( const QString &chan ) +{ + IRCChannelContact *c = manager->existChannel( chan ); + if ( c ) + c->endOfNames(); +} + +void IRCSignalHandler::slotTopicUser(const QString &chan, const QString &user,const QDateTime &time) +{ + IRCChannelContact *c = manager->existChannel( chan ); + if( c ) + c->topicUser( user, time ); +} + +void IRCSignalHandler::slotNewWhoIsIdle(const QString &nick, unsigned long val ) +{ + IRCUserContact *c = manager->findUser( nick ); + if( c ) + c->newWhoIsIdle( val ); +} + +void IRCSignalHandler::slotNewWhoReply(const QString &nick, const QString &arg1, const QString &arg2, + const QString &arg3, const QString &arg4, bool arg5, const QString &arg6, uint arg7, const QString &arg8 ) +{ + IRCUserContact *c = manager->findUser( nick ); + if( c ) + c->newWhoReply( arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 ); +} + +#include "ircsignalhandler.moc" diff --git a/kopete/protocols/irc/ircsignalhandler.h b/kopete/protocols/irc/ircsignalhandler.h new file mode 100644 index 00000000..a87f814c --- /dev/null +++ b/kopete/protocols/irc/ircsignalhandler.h @@ -0,0 +1,334 @@ + +/* + ircsignalhandler.h - Maps signals from the IRC engine to contacts + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2003 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. * + * * + ************************************************************************* +*/ + +#ifndef _IRC_SIGNAL_HANDLER_H +#define _IRC_SIGNAL_HANDLER_H + +#include <qobject.h> +#include <qstringlist.h> +#include <qdatetime.h> + +#include <kdebug.h> + +#include "ircaccount.h" +#include "irccontactmanager.h" + +/*** +* IRC Signal handler. Mapps a KIRC engine signal to the right contact. Avoids +* Having a signal connected to 500+ slots where only one is valid, instead +* uses the contact dictionary. +* +* Warning: This file has a lot of black magic in it. Avoid it if +* you don't want your eyes to bleed. More below... +* +* Define some templated classes and methods to map a KIRC signal to the +* right contact. Having these templates greatly cuts down *A LOT* on the amount of +* code that would need to be in the signal mapper, at the expense of some readability. +* +* There are four IRCSignalMapping classes, one each for signals with 0, 1, 2, +* and 3 arguments ( plus the contact ID ). The classes take the signal, look +* up the contact it is for, and call the function passed into the class by the +* mapping function. +* +* Since QObjects cannot be inside templates, the QMember classes that connect +* to the slots are seperate. +*/ + +/*** Pre-declare mapping types for the QObjects **/ +struct IRCSignalMappingBase{}; + +struct IRCSignalMappingT : IRCSignalMappingBase +{ + virtual void exec( const QString & ) = 0; + virtual ~IRCSignalMappingT() {}; +}; + +struct IRCSignalMappingSingleT : IRCSignalMappingBase +{ + virtual void exec( const QString &, const QString & ) = 0; + virtual ~IRCSignalMappingSingleT() {}; +}; + +struct IRCSignalMappingDoubleT : IRCSignalMappingBase +{ + virtual void exec( const QString &, const QString &, const QString & ) = 0; + virtual ~IRCSignalMappingDoubleT() {}; +}; + +struct IRCSignalMappingTripleT : IRCSignalMappingBase +{ + virtual void exec( const QString &, const QString &, const QString &, const QString & ) = 0; + virtual ~IRCSignalMappingTripleT() {}; +}; + +/*** +QObject members, these connect to the KIRC signals and call +the Mapping functions when they emit. +**/ + +class QMember : public QObject +{ + Q_OBJECT + + public: + QMember( IRCSignalMappingT *m, QObject *p ) : QObject( p ), mapping( m ){}; + + public slots: + void slotEmit( const QString &id ) + { + //kdDebug(14120) << k_funcinfo << id << endl; + mapping->exec(id); + } + + private: + IRCSignalMappingT *mapping; +}; + +class QMemberSingle : public QObject +{ + Q_OBJECT + + public: + QMemberSingle( IRCSignalMappingSingleT *m, QObject *p ) : QObject( p ), mapping( m ){} + + public slots: + void slotEmit( const QString &id, const QString &arg ) + { + //kdDebug(14120) << k_funcinfo << id << " : " << arg << endl; + mapping->exec(id,arg); + } + + private: + IRCSignalMappingSingleT *mapping; +}; + +class QMemberDouble : public QObject +{ + Q_OBJECT + + public: + QMemberDouble( IRCSignalMappingDoubleT *m, QObject *p ) : QObject( p ), mapping( m ){} + + public slots: + void slotEmit( const QString &id, const QString &arg, const QString &arg2 ) + { + //kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << endl; + mapping->exec(id,arg,arg2); + } + + private: + IRCSignalMappingDoubleT *mapping; +}; + +class QMemberTriple : public QObject +{ + Q_OBJECT + + public: + QMemberTriple( IRCSignalMappingTripleT *m, QObject *p ) : QObject( p ), mapping( m ){} + + public slots: + void slotEmit( const QString &id, const QString &arg, const QString &arg2, const QString &arg3 ) + { + //kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << " : " << arg3 << endl; + mapping->exec(id,arg,arg2,arg3); + } + + private: + IRCSignalMappingTripleT *mapping; +}; + +/*** +Mapping classes. These contain pointers to the functions to call. We first +look up the right contact in the contact manager's dictionary, and then +call the method +**/ + +template <class TClass> +class IRCSignalMapping : public IRCSignalMappingT +{ + public: + IRCSignalMapping( IRCContactManager *mgr, const char * /*signal*/, + void (TClass::*m)() ) : manager(mgr), method(m){} + + void exec( const QString &id ) + { + TClass *c = (TClass*)manager->findContact( id ); + if( c ) + { + void (TClass::*tmp)() = (void (TClass::*)())method; + (*c.*tmp)(); + } + } + + private: + IRCContactManager *manager; + void (TClass::*method)(); +}; + +template <class TClass> +class IRCSignalMappingSingle : public IRCSignalMappingSingleT +{ + public: + IRCSignalMappingSingle<TClass>( IRCContactManager *mgr, const char * /*signal*/, + void (TClass::*m)(const QString&) ) : manager(mgr), method(m){} + + void exec( const QString &id, const QString &arg ) + { + TClass *c = (TClass*)manager->findContact( id ); + if( c ) + { + void (TClass::*tmp)(const QString&) = (void (TClass::*)(const QString&))method; + (*c.*tmp)( arg ); + } + } + + private: + IRCContactManager *manager; + void (TClass::*method)(const QString &); +}; + +template <class TClass> +class IRCSignalMappingDouble : public IRCSignalMappingDoubleT +{ + public: + IRCSignalMappingDouble<TClass>( IRCContactManager *mgr, const char * /*signal*/, + void (TClass::*m)(const QString&,const QString&) ) : manager(mgr), method(m){} + + void exec( const QString &id,const QString &arg, const QString &arg2 ) + { + TClass *c = (TClass*)manager->findContact( id ); + if( c ) + { + void (TClass::*tmp)(const QString&,const QString&) = + (void (TClass::*)(const QString&,const QString&))method; + (*c.*tmp)(arg,arg2); + } + } + + private: + IRCContactManager *manager; + void (TClass::*method)(const QString &,const QString &); +}; + +template <class TClass> +class IRCSignalMappingTriple : public IRCSignalMappingTripleT +{ + public: + IRCSignalMappingTriple<TClass>( IRCContactManager *mgr, const char * /*signal*/, + void (TClass::*m)(const QString&,const QString&,const QString&) ) + : manager(mgr), method(m){} + + void exec( const QString &id,const QString&arg, const QString &arg2, const QString &arg3 ) + { + TClass *c = (TClass*)manager->findContact( id ); + if( c ) + { + void (TClass::*tmp)(const QString&,const QString&,const QString&) = + (void (TClass::*)(const QString&,const QString&,const QString&))method; + (*c.*tmp)(arg,arg2,arg3); + } + } + + private: + IRCContactManager *manager; + void (TClass::*method)(const QString &,const QString &,const QString &); +}; + +class IRCSignalHandler : public QObject +{ + Q_OBJECT + + public: + IRCSignalHandler( IRCContactManager *manager ); + ~IRCSignalHandler(); + + private slots: + + /**** + Slots for signals with non-QString types + */ + + //Channel contact slots + void slotNamesList( const QString &, const QStringList & ); + void slotEndOfNames( const QString & ); + void slotTopicUser( const QString &, const QString&, const QDateTime &); + + //User contact slots + void slotNewWhoIsIdle(const QString &, unsigned long ); + void slotNewWhoReply(const QString &, const QString &, const QString &, const QString &, + const QString &, bool , const QString &, uint , const QString & ); + + private: + IRCContactManager *manager; + QValueList<IRCSignalMappingBase*> mappings; + + /**** + Signal mapping functions + */ + + template <class TClass> + inline void map( IRCContactManager *m, const char* signal, void (TClass::*method)() ) + { + IRCSignalMappingT *mapping = new IRCSignalMapping<TClass>( m, signal, method ); + mappings.append(mapping); + QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal, + new QMember( mapping, this), + SLOT( slotEmit( const QString &) ) + ); + } + + template <class TClass> + inline void mapSingle( IRCContactManager *m, + const char* signal, void (TClass::*method)(const QString&) ) + { + IRCSignalMappingSingleT *mapping = new IRCSignalMappingSingle<TClass>( m, signal, method ); + mappings.append(mapping); + QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal, + new QMemberSingle( mapping, this), + SLOT( slotEmit( const QString &, const QString &) ) + ); + } + + template <class TClass> + inline void mapDouble( IRCContactManager *m, + const char* signal, void (TClass::*method)(const QString&,const QString&) ) + { + IRCSignalMappingDoubleT *mapping = new IRCSignalMappingDouble<TClass>( m, signal, method ); + mappings.append(mapping); + QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal, + new QMemberDouble( mapping, this), + SLOT( slotEmit( const QString &, const QString &,const QString &) ) + ); + } + + template <class TClass> + inline void mapTriple( IRCContactManager *m, + const char* signal, + void (TClass::*method)(const QString&,const QString &, const QString &) ) + { + IRCSignalMappingTripleT *mapping = new IRCSignalMappingTriple<TClass>( m, signal, method ); + mappings.append(mapping); + QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal, + new QMemberTriple( mapping, this), + SLOT( slotEmit( const QString &, const QString &,const QString &,const QString &) ) + ); + } +}; + +#endif diff --git a/kopete/protocols/irc/irctransferhandler.cpp b/kopete/protocols/irc/irctransferhandler.cpp new file mode 100644 index 00000000..4715679e --- /dev/null +++ b/kopete/protocols/irc/irctransferhandler.cpp @@ -0,0 +1,183 @@ +/* + irctransferhandler.cpp - IRC transfer. + + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 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 <kdebug.h> + +#include <kopetetransfermanager.h> + +#include "libkirc/kirctransfer.h" +#include "libkirc/kirctransferhandler.h" + +#include "kopetemetacontact.h" +#include "irccontact.h" +#include "irccontactmanager.h" + +#include "irctransferhandler.h" + +IRCTransferHandler *IRCTransferHandler::self() +{ + static IRCTransferHandler sm_self; + return &sm_self; +} + +KIRC::TransferHandler *IRCTransferHandler::handler() +{ + return KIRC::TransferHandler::self(); +} + +IRCTransferHandler::IRCTransferHandler() +{ + connect(handler(), SIGNAL(transferCreated(KIRC::Transfer *)), + this, SLOT(transferCreated(KIRC::Transfer *))); + + connect(Kopete::TransferManager::transferManager(), SIGNAL(accepted(Kopete::Transfer *, const QString &)), + this, SLOT(transferAccepted(Kopete::Transfer *, const QString&))); + connect( Kopete::TransferManager::transferManager(), SIGNAL(refused(const Kopete::FileTransferInfo &)), + this, SLOT(transferRefused(const Kopete::FileTransferInfo &))); +} + +void IRCTransferHandler::transferCreated(KIRC::Transfer *t) +{ + kdDebug(14120) << k_funcinfo << endl; + + IRCContact *contact = IRCContactManager::existContact(t->engine(), t->nick()); + QString fileName = t->fileName(); + unsigned long fileSize = t->fileSize(); + + if(!contact) + { + kdDebug(14120) << k_funcinfo << "Trying to create transfer for a non existing contact(" << t->nick() << ")." << endl; + return; + } + + switch(t->type()) + { +// case KIRC::Transfer::Chat: + case KIRC::Transfer::FileOutgoing: + { + Kopete::Transfer *kt = Kopete::TransferManager::transferManager()->addTransfer( + contact, fileName, fileSize, contact->metaContact()->displayName(), + Kopete::FileTransferInfo::Outgoing); + connectKopeteTransfer(kt, t); + } + break; + case KIRC::Transfer::FileIncoming: + { + int ID = Kopete::TransferManager::transferManager()->askIncomingTransfer( + contact , fileName, fileSize); + m_idMap.insert(ID, t); + } + break; + default: + kdDebug(14120) << k_funcinfo << "Unknown transfer type" << endl; + t->deleteLater(); + } +} + +void IRCTransferHandler::transferAccepted(Kopete::Transfer *kt, const QString &file) +{ + kdDebug(14120) << k_funcinfo << endl; + + KIRC::Transfer *t = getKIRCTransfer(kt->info()); + if(t) + { + t->setFileName(file); + connectKopeteTransfer(kt, t); + } +} +void IRCTransferHandler::transferRefused(const Kopete::FileTransferInfo &info) +{ + kdDebug(14120) << k_funcinfo << endl; + + KIRC::Transfer *t = getKIRCTransfer(info); + if(t) + { + t->deleteLater(); + } +} + +void IRCTransferHandler::connectKopeteTransfer(Kopete::Transfer *kt, KIRC::Transfer *t) +{ + kdDebug(14120) << k_funcinfo << endl; + + if(kt && t) + { + switch(t->type()) + { +// case KIRC::Transfer::Chat: + case KIRC::Transfer::FileOutgoing: + case KIRC::Transfer::FileIncoming: + connect(t , SIGNAL(fileSizeAcknowledge(unsigned int)), + kt, SLOT(slotProcessed(unsigned int))); + break; + default: + kdDebug(14120) << k_funcinfo << "Unknown transfer connections for type" << endl; + t->deleteLater(); + return; + } + + connect(t , SIGNAL(complete()), + kt, SLOT(slotComplete())); + +// connect(kt , SIGNAL(transferCanceled()), +// t, SLOT(abort())); +// connect(kt, SIGNAL(destroyed()), +// t, SLOT(slotKopeteTransferDestroyed())); + + connect(kt, SIGNAL(result(KIO::Job *)), + this , SLOT(kioresult(KIO::Job *))); + + t->initiate(); + } +} + +void IRCTransferHandler::kioresult(KIO::Job *job) +{ + Kopete::Transfer *kt= (Kopete::Transfer *)job; // FIXME: move to *_cast + if(!kt) + { + kdDebug(14120) << k_funcinfo << "Kopete::Transfer not found from kio:" << job << endl; + return; + } + + switch(kt->error()) + { + case 0: // 0 means no error + break; + case KIO::ERR_USER_CANCELED: + kdDebug(14120) << k_funcinfo << "User canceled transfer." << endl; + // KIO::buildErrorString form error don't provide a result string ... +// if (t->) +// kt->userAbort(i18n("User canceled transfer.")); +// else +// kt->userAbort(i18n("User canceled transfer for file:%1").arg(t->fileName())); + break; + default: + kdDebug(14120) << k_funcinfo << "Transfer halted:" << kt->error() << endl; +// kt->userAbort(KIO::buildErrorString(kt->error(), kt->fileName())); + break; + } +} + +KIRC::Transfer *IRCTransferHandler::getKIRCTransfer(const Kopete::FileTransferInfo &info) +{ + KIRC::Transfer *t = m_idMap[info.transferId()]; + m_idMap.remove(info.transferId()); + return t; +} + +#include "irctransferhandler.moc" diff --git a/kopete/protocols/irc/irctransferhandler.h b/kopete/protocols/irc/irctransferhandler.h new file mode 100644 index 00000000..17c419ae --- /dev/null +++ b/kopete/protocols/irc/irctransferhandler.h @@ -0,0 +1,65 @@ +/* + irctransferhandler.h - IRC transfer. + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCTRANSFERHANDLER_H +#define IRCTRANSFERHANDLER_H + +#include <qintdict.h> + +#include <kopetetransfermanager.h> + +namespace Kopete +{ + class Transfer; +} + +namespace KIRC +{ +class Transfer; +class TransferHandler; +} + +class IRCTransferHandler + : public QObject +{ + Q_OBJECT + +public: + static IRCTransferHandler *self(); + +private slots: + void transferCreated(KIRC::Transfer *); + void transferAccepted(Kopete::Transfer *kt, const QString&file); + void transferRefused(const Kopete::FileTransferInfo &info); + + void kioresult(KIO::Job *job); + +private: + IRCTransferHandler(); + + void connectKopeteTransfer(Kopete::Transfer *kt, KIRC::Transfer *t); + + /* warning: After calling this method the KIRC::Transfer is removed from the m_idMap. + */ + KIRC::Transfer *getKIRCTransfer(const Kopete::FileTransferInfo &info); + + KIRC::TransferHandler *handler(); + + QIntDict<KIRC::Transfer> m_idMap; +}; + +#endif diff --git a/kopete/protocols/irc/ircusercontact.cpp b/kopete/protocols/irc/ircusercontact.cpp new file mode 100644 index 00000000..dc9dcbf2 --- /dev/null +++ b/kopete/protocols/irc/ircusercontact.cpp @@ -0,0 +1,734 @@ +/* + ircusercontact.cpp - IRC User Contact + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002 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 "ircusercontact.h" +#include "ircservercontact.h" +#include "ircchannelcontact.h" +#include "irccontactmanager.h" +#include "ircaccount.h" +#include "ircprotocol.h" +#include "kcodecaction.h" + +#include "kopetemetacontact.h" +#include "kopeteview.h" + +#include <kaction.h> +#include <kdebug.h> +#include <kfiledialog.h> +#include <klocale.h> + +#include <qtimer.h> + +IRCUserContact::IRCUserContact(IRCContactManager *contactManager, const QString &nickname, Kopete::MetaContact *m ) + : IRCContact(contactManager, nickname, m ), + actionCtcpMenu(0L) +{ + setFileCapable(true); + + mOnlineTimer = new QTimer( this ); + + QObject::connect(mOnlineTimer, SIGNAL(timeout()), this, SLOT( slotUserOffline() ) ); + + QObject::connect(kircEngine(), SIGNAL(incomingChannelModeChange(const QString&, const QString&, const QString&)), + this, SLOT(slotIncomingModeChange(const QString&,const QString&, const QString&))); + + mInfo.isOperator = false; + mInfo.isIdentified = false; + mInfo.idle = 0; + mInfo.hops = 0; + mInfo.away = false; + mInfo.online = metaContact()->isTemporary(); + + updateStatus(); +} + +void IRCUserContact::updateStatus() +{ + //kdDebug(14120) << k_funcinfo << endl; + + Kopete::OnlineStatus newStatus; + + switch (kircEngine()->status()) + { + case KIRC::Engine::Idle: + newStatus = m_protocol->m_UserStatusOffline; + break; + + case KIRC::Engine::Connecting: + case KIRC::Engine::Authentifying: + if (this == ircAccount()->mySelf()) + newStatus = m_protocol->m_UserStatusConnecting; + else + newStatus = m_protocol->m_UserStatusOffline; + break; + + case KIRC::Engine::Connected: + case KIRC::Engine::Closing: + if (mInfo.away) + newStatus = m_protocol->m_UserStatusAway; + else if (mInfo.online) + newStatus = m_protocol->m_UserStatusOnline; + break; + + default: + newStatus = m_protocol->m_StatusUnknown; + } + + // Try hard not to emit several onlineStatusChanged() signals. + bool onlineStatusChanged = false; + + + /* The away status is global, so if the user goes away, we must set + * the new status on all channels. + */ + + + // This may not be created yet ( for myself() on startup ) + if( ircAccount()->contactManager() ) + { + QValueList<IRCChannelContact*> channels = ircAccount()->contactManager()->findChannelsByMember(this); + + for( QValueList<IRCChannelContact*>::iterator it = channels.begin(); it != channels.end(); ++it ) + { + IRCChannelContact *channel = *it; + Kopete::OnlineStatus currentStatus = channel->manager()->contactOnlineStatus(this); + + //kdDebug(14120) << k_funcinfo << "iterating channel " << channel->nickName() << " internal status: " << currentStatus.internalStatus() << endl; + + if( currentStatus.internalStatus() >= IRCProtocol::Online ) + { + onlineStatusChanged = true; + + if( !(currentStatus.internalStatus() & IRCProtocol::Away) && newStatus == m_protocol->m_UserStatusAway ) + { + setOnlineStatus( newStatus ); + //kdDebug(14120) << k_funcinfo << "was NOT away, but is now, channel " << channel->nickName() << endl; + adjustInternalOnlineStatusBits(channel, IRCProtocol::Away, AddBits); + } + else if( (currentStatus.internalStatus() & IRCProtocol::Away) && newStatus == m_protocol->m_UserStatusOnline ) + { + setOnlineStatus( newStatus ); + //kdDebug(14120) << k_funcinfo << "was away, but not anymore, channel " << channel->nickName() << endl; + adjustInternalOnlineStatusBits(channel, IRCProtocol::Away, RemoveBits); + + } + else if( newStatus.internalStatus() < IRCProtocol::Away ) + { + //kdDebug(14120) << k_funcinfo << "offline or connecting?" << endl; + channel->manager()->setContactOnlineStatus( this, newStatus ); + } + } + } + } + + if (!onlineStatusChanged) { + //kdDebug(14120) << k_funcinfo << "setting status at last" << endl; + setOnlineStatus( newStatus ); + } +} + +void IRCUserContact::sendFile(const KURL &sourceURL, const QString&, unsigned int) +{ + QString filePath; + + //If the file location is null, then get it from a file open dialog + if( !sourceURL.isValid() ) + filePath = KFileDialog::getOpenFileName(QString::null, "*", 0l , i18n("Kopete File Transfer")); + else + filePath = sourceURL.path(-1); + + kdDebug(14120) << k_funcinfo << "File chosen to send:" << filePath << endl; + + if (!filePath.isEmpty()) + kircEngine()->CtcpRequest_dcc( m_nickName, filePath, 0, KIRC::Transfer::FileOutgoing); +} + +void IRCUserContact::slotUserOffline() +{ + mInfo.online = false; + mInfo.away = false; + + updateStatus(); + + if( !metaContact()->isTemporary() ) + kircEngine()->writeMessage( QString::fromLatin1("WHOWAS %1").arg(m_nickName) ); + + removeProperty( m_protocol->propUserInfo ); + removeProperty( m_protocol->propServer ); + removeProperty( m_protocol->propChannels ); +} + +void IRCUserContact::setAway(bool isAway) +{ + //kdDebug(14120) << k_funcinfo << isAway << endl; + + mInfo.away = isAway; + updateStatus(); +} + +void IRCUserContact::incomingUserIsAway(const QString &reason) +{ + if( manager( Kopete::Contact::CannotCreate ) ) + { + Kopete::Message msg( (Kopete::Contact*)ircAccount()->myServer(), mMyself, + i18n("%1 is away (%2)").arg( m_nickName ).arg( reason ), + Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW ); + manager(Kopete::Contact::CanCreate)->appendMessage(msg); + } +} + +void IRCUserContact::userOnline() +{ + mInfo.online = true; + updateStatus(); + if (this != ircAccount()->mySelf() && !metaContact()->isTemporary() && ircAccount()->isConnected()) + { + mOnlineTimer->start( 45000, true ); + ircAccount()->setCurrentCommandSource(0); + kircEngine()->whois(m_nickName); + } + + removeProperty( m_protocol->propLastSeen ); +} + +void IRCUserContact::slotUserInfo() +{ + if (isChatting()) + { + ircAccount()->setCurrentCommandSource(manager()); + kircEngine()->whois(m_nickName); + } +} + +const QString IRCUserContact::caption() const +{ + return i18n("%1 @ %2").arg(m_nickName).arg(kircEngine()->currentHost()); +} + +void IRCUserContact::slotOp() +{ + contactMode( QString::fromLatin1("+o") ); +} + +void IRCUserContact::slotDeop() +{ + contactMode( QString::fromLatin1("-o") ); +} + +void IRCUserContact::slotVoice() +{ + contactMode( QString::fromLatin1("+v") ); +} + +void IRCUserContact::slotDevoice() +{ + contactMode( QString::fromLatin1("-v") ); +} + +void IRCUserContact::slotBanHost() +{ + // MODE #foofoofoo +b *!*@host.domain.net + + if (mInfo.hostName.isEmpty()) { + if (kircEngine()->isConnected()) { + kircEngine()->whois(m_nickName); + QTimer::singleShot( 750, this, SLOT( slotBanHostOnce() ) ); + } + } else { + slotBanHostOnce(); + } +} +void IRCUserContact::slotBanHostOnce() +{ + if (mInfo.hostName.isEmpty()) + return; + + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + + kircEngine()->mode(channelName, QString::fromLatin1("+b *!*@%1").arg(mInfo.hostName)); +} + +void IRCUserContact::slotBanUserHost() +{ + // MODE #foofoofoo +b *!*user@host.domain.net + + if (mInfo.hostName.isEmpty()) { + if (kircEngine()->isConnected()) { + kircEngine()->whois(m_nickName); + QTimer::singleShot( 750, this, SLOT( slotBanUserHostOnce() ) ); + } + } else { + slotBanUserHostOnce(); + } +} +void IRCUserContact::slotBanUserHostOnce() +{ + if (mInfo.hostName.isEmpty()) + return; + + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + + kircEngine()->mode(channelName, QString::fromLatin1("+b *!*%1@%2").arg(mInfo.userName, mInfo.hostName)); +} + +void IRCUserContact::slotBanDomain() +{ + // MODE #foofoofoo +b *!*@*.domain.net + + if (mInfo.hostName.isEmpty()) { + if (kircEngine()->isConnected()) { + kircEngine()->whois(m_nickName); + QTimer::singleShot( 750, this, SLOT( slotBanDomainOnce() ) ); + } + } else { + slotBanDomainOnce(); + } +} +void IRCUserContact::slotBanDomainOnce() +{ + if (mInfo.hostName.isEmpty()) + return; + + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + + QString domain = mInfo.hostName.section('.', 1); + + kircEngine()->mode(channelName, QString::fromLatin1("+b *!*@*.%1").arg(domain)); +} + +void IRCUserContact::slotBanUserDomain() +{ + // MODE #foofoofoo +b *!*user@*.domain.net + + if (mInfo.hostName.isEmpty()) { + if (kircEngine()->isConnected()) { + kircEngine()->whois(m_nickName); + QTimer::singleShot( 750, this, SLOT( slotBanUserDomainOnce() ) ); + } + } else { + slotBanUserDomainOnce(); + } +} +void IRCUserContact::slotBanUserDomainOnce() +{ + if (mInfo.hostName.isEmpty()) + return; + + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + + QString domain = mInfo.hostName.section('.', 1); + + kircEngine()->mode(channelName, QString::fromLatin1("+b *!*%1@*.%2").arg(mInfo.userName, domain)); +} + +void IRCUserContact::slotKick() +{ + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + kircEngine()->kick(m_nickName, channelName, QString::null); +} + +void IRCUserContact::contactMode(const QString &mode) +{ + Kopete::ContactPtrList members = mActiveManager->members(); + QString channelName = static_cast<IRCContact*>(members.first())->nickName(); + kircEngine()->mode(channelName, QString::fromLatin1("%1 %2").arg(mode).arg(m_nickName)); +} + +void IRCUserContact::slotCtcpPing() +{ + kircEngine()->CtcpRequest_ping(m_nickName); +} + +void IRCUserContact::slotCtcpVersion() +{ + kircEngine()->CtcpRequest_version(m_nickName); +} + +void IRCUserContact::newWhoIsUser(const QString &username, const QString &hostname, const QString &realname) +{ + mInfo.channels.clear(); + mInfo.userName = username; + mInfo.hostName = hostname; + mInfo.realName = realname; + + if( onlineStatus().status() == Kopete::OnlineStatus::Offline ) + { + setProperty( m_protocol->propUserInfo, QString::fromLatin1("%1@%2") + .arg(mInfo.userName).arg(mInfo.hostName) ); + setProperty( m_protocol->propServer, mInfo.serverName ); + setProperty( m_protocol->propFullName, mInfo.realName ); + } +} + +void IRCUserContact::newWhoIsServer(const QString &servername, const QString &serverinfo) +{ + mInfo.serverName = servername; + if( metaContact()->isTemporary() || onlineStatus().status() == Kopete::OnlineStatus::Online + || onlineStatus().status() == Kopete::OnlineStatus::Away ) + mInfo.serverInfo = serverinfo; + else + { + //kdDebug(14120)<< "Setting last online: " << serverinfo << endl; + + // Try to convert first, since server can return depending if + // user is online or not: + // + // 312 mynick othernick localhost.localdomain :FooNet Server + // 312 mynick othernick localhost.localdomain :Thu Jun 16 21:00:36 2005 + + QDateTime lastSeen = QDateTime::fromString( serverinfo ); + if( lastSeen.isValid() ) + setProperty( m_protocol->propLastSeen, lastSeen ); + } +} + +void IRCUserContact::newWhoIsIdle(unsigned long idle) +{ + mInfo.idle = idle; +} + +void IRCUserContact::newWhoIsOperator() +{ + mInfo.isOperator = true; +} + +void IRCUserContact::newWhoIsIdentified() +{ + mInfo.isIdentified = true; + setProperty( m_protocol->propIsIdentified, i18n("True") ); +} + +void IRCUserContact::newWhoIsChannels(const QString &channel) +{ + mInfo.channels.append( channel ); +} + +void IRCUserContact::whoIsComplete() +{ + Kopete::ChatSession *commandSource = ircAccount()->currentCommandSource(); + + updateInfo(); + + if( isChatting() && commandSource && + commandSource == manager(Kopete::Contact::CannotCreate) ) + { + //User info + QString msg = i18n("%1 is (%2@%3): %4<br/>") + .arg(m_nickName) + .arg(mInfo.userName) + .arg(mInfo.hostName) + .arg(mInfo.realName); + + if( mInfo.isIdentified ) + msg += i18n("%1 is authenticated with NICKSERV<br/>").arg(m_nickName); + + if( mInfo.isOperator ) + msg += i18n("%1 is an IRC operator<br/>").arg(m_nickName); + + //Channels + msg += i18n("on channels %1<br/>").arg(mInfo.channels.join(" ; ")); + + //Server + msg += i18n("on IRC via server %1 ( %2 )<br/>").arg(mInfo.serverName).arg(mInfo.serverInfo); + + //Idle + QString idleTime = formattedIdleTime(); + msg += i18n("idle: %2<br/>").arg( idleTime.isEmpty() ? QString::number(0) : idleTime ); + + //End + ircAccount()->appendMessage(msg, IRCAccount::InfoReply ); + ircAccount()->setCurrentCommandSource(0); + } +} + +void IRCUserContact::whoWasComplete() +{ + if( isChatting() && ircAccount()->currentCommandSource() == manager() ) + { + //User info + QString msg = i18n("%1 was (%2@%3): %4\n") + .arg(m_nickName) + .arg(mInfo.userName) + .arg(mInfo.hostName) + .arg(mInfo.realName); + + msg += i18n("Last Online: %1\n").arg( + KGlobal::locale()->formatDateTime( + property( m_protocol->propLastSeen ).value().toDateTime() + ) + ); + + ircAccount()->appendMessage(msg, IRCAccount::InfoReply ); + ircAccount()->setCurrentCommandSource(0); + } +} + +QString IRCUserContact::formattedName() const +{ + return mInfo.realName; +} + +void IRCUserContact::updateInfo() +{ + setProperty( m_protocol->propUserInfo, QString::fromLatin1("%1@%2") + .arg(mInfo.userName).arg(mInfo.hostName) ); + setProperty( m_protocol->propServer, mInfo.serverName ); + setProperty( m_protocol->propChannels, mInfo.channels.join(" ") ); + setProperty( m_protocol->propHops, QString::number(mInfo.hops) ); + setProperty( m_protocol->propFullName, mInfo.realName ); + + setIdleTime( mInfo.idle ); + + mInfo.lastUpdate = QTime::currentTime(); +} + +void IRCUserContact::newWhoReply( const QString &channel, const QString &user, const QString &host, + const QString &server, bool away, const QString &flags, uint hops, const QString &realName ) +{ + if( !mInfo.channels.contains( channel ) ) + mInfo.channels.append( channel ); + + mInfo.userName = user; + mInfo.hostName = host; + mInfo.serverName = server; + mInfo.flags = flags; + mInfo.hops = hops; + mInfo.realName = realName; + + setAway(away); + + updateInfo(); + + if( isChatting() && ircAccount()->currentCommandSource() == manager() ) + { + ircAccount()->setCurrentCommandSource(0); + } +} + +QPtrList<KAction> *IRCUserContact::customContextMenuActions( Kopete::ChatSession *manager ) +{ + if( manager ) + { + QPtrList<KAction> *mCustomActions = new QPtrList<KAction> (); + mActiveManager = manager; + Kopete::ContactPtrList members = mActiveManager->members(); + IRCChannelContact *isChannel = dynamic_cast<IRCChannelContact*>( members.first() ); + + if( !actionCtcpMenu ) + { + actionCtcpMenu = new KActionMenu(i18n("C&TCP"), 0, this ); + actionCtcpMenu->insert( new KAction(i18n("&Version"), 0, this, + SLOT(slotCtcpVersion()), actionCtcpMenu) ); + actionCtcpMenu->insert( new KAction(i18n("&Ping"), 0, this, + SLOT(slotCtcpPing()), actionCtcpMenu) ); + + actionModeMenu = new KActionMenu(i18n("&Modes"), 0, this, "actionModeMenu"); + actionModeMenu->insert( new KAction(i18n("&Op"), 0, this, + SLOT(slotOp()), actionModeMenu, "actionOp") ); + actionModeMenu->insert( new KAction(i18n("&Deop"), 0, this, + SLOT(slotDeop()), actionModeMenu, "actionDeop") ); + actionModeMenu->insert( new KAction(i18n("&Voice"), 0, this, + SLOT(slotVoice()), actionModeMenu, "actionVoice") ); + actionModeMenu->insert( new KAction(i18n("Devoice"), 0, this, + SLOT(slotDevoice()), actionModeMenu, "actionDevoice") ); + actionModeMenu->setEnabled( false ); + + actionKick = new KAction(i18n("&Kick"), 0, this, SLOT(slotKick()), this); + actionKick->setEnabled( false ); + + actionBanMenu = new KActionMenu(i18n("&Ban"), 0, this, "actionBanMenu"); + actionBanMenu->insert( new KAction(i18n("Host (*!*@host.domain.net)"), 0, this, + SLOT(slotBanHost()), actionBanMenu ) ); + actionBanMenu->insert( new KAction(i18n("Domain (*!*@*.domain.net)"), 0, this, + SLOT(slotBanDomain()), actionBanMenu ) ); + actionBanMenu->insert( new KAction(i18n("User@Host (*!*user@host.domain.net)"), 0, this, + SLOT(slotBanUserHost()), actionBanMenu ) ); + actionBanMenu->insert( new KAction(i18n("User@Domain (*!*user@*.domain.net)"), 0, this, + SLOT(slotBanUserDomain()), actionBanMenu ) ); + actionBanMenu->setEnabled( false ); + + codecAction = new KCodecAction( i18n("&Encoding"), 0, this, "selectcharset" ); + connect( codecAction, SIGNAL( activated( const QTextCodec * ) ), + this, SLOT( setCodec( const QTextCodec *) ) ); + codecAction->setCodec( codec() ); + } + + mCustomActions->append( actionCtcpMenu ); + mCustomActions->append( actionModeMenu ); + mCustomActions->append( actionKick ); + mCustomActions->append( actionBanMenu ); + mCustomActions->append( codecAction ); + + if( isChannel ) + { + bool isOperator = ( manager->contactOnlineStatus( account()->myself() ).internalStatus() & IRCProtocol::Operator ); + actionModeMenu->setEnabled(isOperator); + actionBanMenu->setEnabled(isOperator); + actionKick->setEnabled(isOperator); + } + + return mCustomActions; + } + + mActiveManager = 0L; + + return 0L; +} + +void IRCUserContact::slotIncomingModeChange( const QString &channel, const QString &, const QString &mode_ ) +{ + kdDebug(14120) << k_funcinfo << "channel: " << channel << " mode: " << mode_ << endl; + + IRCChannelContact *chan = ircAccount()->contactManager()->findChannel( channel ); + + if( !chan->locateUser( m_nickName ) ) + return; + + // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o kakkonen + // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o-o foobar001 kakkonen + // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +oo kakkonen foobar001 + // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o-ov foobar001 kakkonen foobar001 + // + // irssi manual example: /MODE #channel +nto-o+v nick1 nick2 nick3 + + QStringList users = QStringList::split(' ', mode_); + users.pop_front(); + + const QString mode = mode_.section(' ', 0, 0); + + bitAdjustment adjMode = RemoveBits; + QStringList::iterator user = users.begin(); + + //kdDebug(14120) << "me: " << m_nickName << " users: " << users << " mode: " << mode << endl; + + for( uint i=0; i < mode.length(); i++ ) + { + switch( mode[i] ) + { + case '+': + adjMode = AddBits; + break; + + case '-': + adjMode = RemoveBits; + break; + + default: + //kdDebug(14120) << "got " << mode[i] << ", user: " << *user << endl; + + if (mode[i] == 'o') { + if (user == users.end()) + return; + + if ((*user).lower() == m_nickName.lower()) + adjustInternalOnlineStatusBits(chan, IRCProtocol::Operator, adjMode); + + ++user; + } + else if (mode[i] == 'v') { + if (user == users.end()) + return; + + if ((*user).lower() == m_nickName.lower()) + adjustInternalOnlineStatusBits(chan, IRCProtocol::Voiced, adjMode); + + ++user; + } + + break; + } + } +} + + +/* Remove or add the given bits for the given channel from the current internal online status. + * + * You could fiddle with bits like IRCProtocol::Operator, IRCProtocol::Voiced, etc. + */ + +void IRCUserContact::adjustInternalOnlineStatusBits(IRCChannelContact *channel, unsigned statusAdjustment, bitAdjustment adj) +{ + Kopete::OnlineStatus currentStatus = channel->manager()->contactOnlineStatus(this); + Kopete::OnlineStatus newStatus; + + if (adj == RemoveBits) { + + // If the bit is not set in the current internal status, stop here. + if ((currentStatus.internalStatus() & ~statusAdjustment) == currentStatus.internalStatus()) + return; + + newStatus = m_protocol->statusLookup( + (IRCProtocol::IRCStatus)(currentStatus.internalStatus() & ~statusAdjustment) + ); + + } else if (adj == AddBits) { + + // If the bit is already set in the current internal status, stop here. + if ((currentStatus.internalStatus() | statusAdjustment) == currentStatus.internalStatus()) + return; + + newStatus = m_protocol->statusLookup( + (IRCProtocol::IRCStatus)(currentStatus.internalStatus() | statusAdjustment) + ); + + } + + channel->manager()->setContactOnlineStatus(this, newStatus); +} + +void IRCUserContact::privateMessage(IRCContact *from, IRCContact *to, const QString &message) +{ + if (to == this) + { + if(to==account()->myself()) + { + Kopete::Message msg(from, from->manager(Kopete::Contact::CanCreate)->members(), message, + Kopete::Message::Inbound, Kopete::Message::RichText, CHAT_VIEW); + from->appendMessage(msg); + } + else + { + kdDebug(14120) << "IRC Server error: Received a private message for " << to->nickName() << ":" << message << endl; + // emit/call something on main ircservercontact + } + } +} + +void IRCUserContact::newAction(const QString &to, const QString &action) +{ + IRCAccount *account = ircAccount(); + + IRCContact *t = account->contactManager()->findUser(to); + + Kopete::Message::MessageDirection dir = + (this == account->mySelf()) ? Kopete::Message::Outbound : Kopete::Message::Inbound; + Kopete::Message msg(this, t, action, dir, Kopete::Message::RichText, + CHAT_VIEW, Kopete::Message::TypeAction); + + //Either this is from me to a guy, or from a guy to me. Either way its a PM + if (dir == Kopete::Message::Outbound) + t->appendMessage(msg); + else + appendMessage(msg); +} + +#include "ircusercontact.moc" diff --git a/kopete/protocols/irc/ircusercontact.h b/kopete/protocols/irc/ircusercontact.h new file mode 100644 index 00000000..3373fa9c --- /dev/null +++ b/kopete/protocols/irc/ircusercontact.h @@ -0,0 +1,146 @@ +/* + ircusercontact.h - IRC User Contact + + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org + + Kopete (c) 2002 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. * + * * + ************************************************************************* +*/ + +#ifndef IRCUSERCONTACT_H +#define IRCUSERCONTACT_H + +#include "kopetechatsessionmanager.h" +#include "irccontact.h" +#include "kopeteonlinestatus.h" + +class QTimer; + +class KActionCollection; +class KAction; +class KActionMenu; +class KCodecAction; + +class IRCContactManager; +class IRCChannelContact; + +struct IRCUserInfo +{ + QString userName; + QString hostName; + QString realName; + QString serverName; + QString serverInfo; + QString flags; + QStringList channels; + unsigned long idle; + bool isOperator; + bool isIdentified; + bool away; + bool online; + uint hops; + QDateTime lastOnline; + QTime lastUpdate; +}; + +/** + * @author Jason Keirstead <jason@keirstead.org + * + * This class is the @ref Kopete::Contact object representing IRC Users, not channels. + * It is derrived from IRCContact where much of its functionality is shared with @ref IRCChannelContact. + */ +class IRCUserContact : public IRCContact +{ + Q_OBJECT + +public: + // This class provides a Kopete::Contact for each user on the channel. + IRCUserContact(IRCContactManager *, const QString &nickname, Kopete::MetaContact *mc); + + // Kopete::Contact stuff + virtual QPtrList<KAction> *customContextMenuActions( Kopete::ChatSession *manager ); + virtual const QString caption() const; + + void setAway(bool isAway); + + QString formattedName() const; + + //Methods handled by the signal mapper + void incomingUserIsAway(const QString &message ); + void userOnline(); + void newAction( const QString &from, const QString &action ); + void newWhoIsUser(const QString &username, const QString &hostname, const QString &realname); + void newWhoIsServer(const QString &server, const QString &serverInfo); + void newWhoIsOperator(); + void newWhoIsIdentified(); + void newWhoIsIdle(unsigned long seconds); + void newWhoIsChannels(const QString &channel); + void whoIsComplete(); + void whoWasComplete(); + void newWhoReply( const QString &channel, const QString &user, const QString &host, + const QString &server, bool away, const QString &flags, uint hops, + const QString &realName ); + +public slots: + /** \brief Updates online status for channels based on current internal status. + */ + virtual void updateStatus(); + + virtual void sendFile(const KURL &sourceURL, const QString&, unsigned int); + +protected slots: + virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message); + +private slots: + void slotOp(); + void slotDeop(); + void slotVoice(); + void slotDevoice(); + void slotCtcpPing(); + void slotCtcpVersion(); + void slotBanHost(); + void slotBanUserHost(); + void slotBanDomain(); + void slotBanUserDomain(); + void slotKick(); + void slotUserOffline(); + + void slotBanHostOnce(); + void slotBanUserHostOnce(); + void slotBanDomainOnce(); + void slotBanUserDomainOnce(); + + virtual void slotUserInfo(); + + //This can't be handled by the contact manager since + void slotIncomingModeChange(const QString &nick, const QString &channel, const QString &mode); + +private: + enum bitAdjustment { RemoveBits, AddBits }; + void adjustInternalOnlineStatusBits(IRCChannelContact *channel, unsigned statusAdjustment, bitAdjustment adj); + + void contactMode(const QString &mode); + void updateInfo(); + + KActionMenu *actionModeMenu; + KActionMenu *actionCtcpMenu; + KAction *actionKick; + KActionMenu *actionBanMenu; + KCodecAction *codecAction; + Kopete::ChatSession *mActiveManager; + QTimer *mOnlineTimer; + IRCUserInfo mInfo; +}; + +#endif + +// vim: set noet ts=4 sts=4 tw=4: diff --git a/kopete/protocols/irc/kcodecaction.cpp b/kopete/protocols/irc/kcodecaction.cpp new file mode 100644 index 00000000..e32a1787 --- /dev/null +++ b/kopete/protocols/irc/kcodecaction.cpp @@ -0,0 +1,87 @@ +/* + kcodecaction.cpp + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Kopete (c) 2003-2005 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 <qstringlist.h> +#include <qtextcodec.h> +#include <kcharsets.h> + +#include "kcodecaction.h" + +KCodecAction::KCodecAction( const QString &text, const KShortcut &cut, + QObject *parent, const char *name ) : KSelectAction( text, "", cut, parent, name ) +{ + QObject::connect( this, SIGNAL( activated( const QString & ) ), + this, SLOT( slotActivated( const QString & ) ) ); + + setItems( KCodecAction::supportedEncodings() ); +} + +void KCodecAction::slotActivated( const QString & text ) +{ + /* text is something like "Western European ( iso-8859-1 )", but we must give + * codecForName() only the "iso-8859-1" part. + */ + QString encoding = KGlobal::charsets()->encodingForName(text); + + emit activated( KGlobal::charsets()->codecForName(encoding) ); +} + +void KCodecAction::setCodec( const QTextCodec *codec ) +{ + QStringList items = this->items(); + int i = 0; + for (QStringList::ConstIterator it = items.begin(), end = items.end(); it != end; ++it, ++i) { + QString encoding = KGlobal::charsets()->encodingForName(*it); + + if (KGlobal::charsets()->codecForName(encoding)->mibEnum() == codec->mibEnum()) { + setCurrentItem(i); + break; + } + } +} + +/* Create a list of supported encodings, and keep only one of each encoding + * mime name. + * + * This piece of code from kdepim/kmail/kmmsgbase.cpp + */ + +QStringList KCodecAction::supportedEncodings(bool usAscii) +{ + QStringList encodingNames = KGlobal::charsets()->availableEncodingNames(); + QStringList encodings; + QMap<QString, bool> mimeNames; + + for (QStringList::ConstIterator it = encodingNames.begin(); + it != encodingNames.end(); ++it) + { + QTextCodec *codec = KGlobal::charsets()->codecForName(*it); + QString mimeName = (codec) ? QString(codec->mimeName()).lower() : (*it); + if (mimeNames.find(mimeName) == mimeNames.end()) + { + encodings.append(KGlobal::charsets()->languageForEncoding(*it) + + " ( " + mimeName + " )"); + mimeNames.insert(mimeName, true); + } + } + + encodings.sort(); + if (usAscii) encodings.prepend(KGlobal::charsets() + ->languageForEncoding("us-ascii") + " ( us-ascii )"); + return encodings; +} + +#include "kcodecaction.moc" diff --git a/kopete/protocols/irc/kcodecaction.h b/kopete/protocols/irc/kcodecaction.h new file mode 100644 index 00000000..93f9d6c1 --- /dev/null +++ b/kopete/protocols/irc/kcodecaction.h @@ -0,0 +1,47 @@ +/* + kcodecaction.h + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Kopete (c) 2003-2005 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. * + * * + ************************************************************************* +*/ +#ifndef KCODECACTION_H +#define KCODECACTION_H + +#include <kdeversion.h> +#include <qintdict.h> + +#if KDE_IS_VERSION( 3, 1, 90 ) + #include <kactionclasses.h> +#else + #include <kaction.h> +#endif + +class KCodecAction : public KSelectAction +{ + Q_OBJECT + public: + KCodecAction( const QString &text, const KShortcut &cut = KShortcut(), + QObject *parent = 0, const char *name = 0 ); + + void setCodec( const QTextCodec *codec ); + + static QStringList supportedEncodings( bool usAscii = false ); + + signals: + void activated( const QTextCodec * ); + + private slots: + void slotActivated( const QString & ); +}; + +#endif diff --git a/kopete/protocols/irc/kopete_irc.desktop b/kopete/protocols/irc/kopete_irc.desktop new file mode 100644 index 00000000..6e3cf144 --- /dev/null +++ b/kopete/protocols/irc/kopete_irc.desktop @@ -0,0 +1,79 @@ +[Desktop Entry] +Type=Service +X-Kopete-Version=1000900 +Icon=irc_protocol +ServiceTypes=Kopete/Protocol +X-KDE-Library=kopete_irc +X-Kopete-Messaging-Protocol=messaging/irc +X-KDE-PluginInfo-Author=Kopete Developers +X-KDE-PluginInfo-Email=kopete-devel@kde.org +X-KDE-PluginInfo-Name=kopete_irc +X-KDE-PluginInfo-Version=0.8.0 +X-KDE-PluginInfo-Website=http://kopete.kde.org +X-KDE-PluginInfo-Category=Protocols +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +Name=IRC +Name[bn]=আই-আর-সি +Name[hi]=आईआरसी +Name[ne]=आइआरसी +Comment=Protocol to connect to IRC +Comment[ar]=البروتوكل سيتصل بـ IRC +Comment[be]=Пратакол IRC +Comment[bg]=Протокол за връзка с IRC +Comment[bn]=আই-আর-সিতে সংযোগ করতে প্রোটোকল +Comment[br]=Komenad kevreañ ouzh IRC +Comment[bs]=IRC protokol +Comment[ca]=Protocol per a connectar-se a l'IRC +Comment[cs]=Protokol k připojení k IRC +Comment[cy]=Protocol i gysylltu ag IRC +Comment[da]=Protokol til at forbinde til IRC +Comment[de]=Protokoll zur Verbindung mit IRC +Comment[el]=Πρωτόκολλο για σύνδεση στο IRC +Comment[es]=Protocolo de conexión al IRC +Comment[et]=Protokoll ühendumiseks IRC-ga +Comment[eu]=IRC-ra konektatzeko protokoloa +Comment[fa]=قرار داد برای اتصال به IRC +Comment[fi]=Yhteyskäytänötö IRC-verkkoon kytkeytymiseen +Comment[fr]=Protocole pour se connecter sur IRC +Comment[ga]=Prótacal chun ceangal le IRC +Comment[gl]=Protocolo para conectar a IRC +Comment[he]=פרוטוקול התחברות ל- IRC +Comment[hi]=आईआरसी से जुड़ने का प्रोटोकॉल +Comment[hr]=Protokol za povezivanje na IRC +Comment[hu]=Protokoll az IRC használatához +Comment[is]=Samskiptamáti til að tengjast IRC +Comment[it]=Protocollo per connessione a IRC +Comment[ja]=IRC に接続するプロトコル +Comment[ka]=IRC-თან დაკავშირების ოქმი +Comment[kk]=IRC-ге қосылу протоколы +Comment[km]=ពិធីការភ្ជាប់ទៅ IRC +Comment[lt]=Protokolas prisijungimui prie IRC +Comment[mk]=Протокол за поврзување на IRC +Comment[nb]=Protokoll for å koble til IRC +Comment[nds]=Protokoll för't Tokoppeln na IRC +Comment[ne]=आइआरसी मा जडान गर्ने प्रोटोकल +Comment[nl]=Protocol voor Internet Relay Chat (IRC) +Comment[nn]=Protokoll for å kopla til IRC +Comment[pl]=Protokół połączenia z serwerem IRC +Comment[pt]=Protocolo para ligar ao IRC +Comment[pt_BR]=Protocolo de conexão ao IRC +Comment[ro]=Protocol de conectare la IRC +Comment[ru]=Протокол для подключения к IRC +Comment[sk]=Protokol pre pripojenie k IRC +Comment[sl]=Protokol za povezavo na IRC +Comment[sr]=Протокол за повезивање на IRC +Comment[sr@Latn]=Protokol za povezivanje na IRC +Comment[sv]=Protokoll för att ansluta till IRC +Comment[ta]=IRC உடன் இணைக்க விதிமுறை +Comment[tg]=Қарордоди пайвастшавӣ ба IRC +Comment[tr]=IRC'ye bağlantı iletişim kuralı +Comment[uk]=Протокол для з'єднання з IRC +Comment[uz]=IRC bilan aloqa oʻrnatish uchun protokol +Comment[uz@cyrillic]=IRC билан алоқа ўрнатиш учун протокол +Comment[wa]=Protocole po s' raloyî so les canås IRC +Comment[zh_CN]=连接到 IRC 协议 +Comment[zh_HK]=用來連接至 IRC 的通訊協定 +Comment[zh_TW]=連線到 IRC 的協定 + diff --git a/kopete/protocols/irc/ksparser.cpp b/kopete/protocols/irc/ksparser.cpp new file mode 100644 index 00000000..c101a79e --- /dev/null +++ b/kopete/protocols/irc/ksparser.cpp @@ -0,0 +1,265 @@ +/* This file is part of ksirc + Copyright (c) 2001 Malte Starostik <malte@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* +Color parser code courtesy of ksirc <http://www.kde.org> +Modified by Jason Keirstead <jason@keirstead.org> +*/ + +#include <knotifyclient.h> +#include <kdebug.h> +#include <qbuffer.h> +#include <qdatastream.h> +#include <qstylesheet.h> +#include "ksparser.h" +#include <stdlib.h> + +KSParser KSParser::m_parser; + +const QColor KSParser::IRC_Colors[17]= +{ + Qt::white, + Qt::black, + Qt::darkBlue, + Qt::darkGreen, + Qt::red, + Qt::darkRed, + Qt::darkMagenta, + Qt::darkYellow, + Qt::yellow, + Qt::green, + Qt::darkCyan, + Qt::cyan, + Qt::blue, + Qt::magenta, + Qt::darkGray, + Qt::gray, + QColor() // default invalid color, must be the last +}; + +const QRegExp KSParser::sm_colorsModeRegexp("(\\d{1,2})(?:,(\\d{1,2}))?"); + +template <typename _TYPE_> + inline void swap(_TYPE_ &o1, _TYPE_ &o2) +{ + _TYPE_ tmp = o1; + o1 = o2; + o2 = tmp; +} + +KSParser::KSParser() +{ + kdDebug(14120) << k_funcinfo << endl; +} + +KSParser::~KSParser() +{ + kdDebug(14120) << k_funcinfo << endl; +} + +/* NOTE: If thread corruption are seen simply ad a qlock here */ +QCString KSParser::parse(const QCString &message) +{ + return m_parser._parse(message); +} + +QCString KSParser::_parse(const QCString &message) +{ + QCString data( message.size() * 2 ); + QBuffer buff( data ); + buff.open( IO_WriteOnly ); + + m_tags.clear(); + m_attributes.clear(); + + QRegExp colorsModeRegexp(sm_colorsModeRegexp); + + // should be set to the current default colors .... + QColor fgColor; /*KopeteMesage::fg().name()*/ + QColor bgColor; /*KopeteMesage::bg().name()*/ + + uint chars = 0; + for(uint i = 0; i < message.length(); ++i) + { + const QChar &cur = message[i]; + QString toAppend; + + switch (cur) + { + case 0x02: //Bold: ^B + toAppend= toggleTag("b"); + break; + case 0x03: //Color code: ^C + if (colorsModeRegexp.search(message, i+1) == (int)i+1) + { + i += colorsModeRegexp.matchedLength(); // + 1 will be added by ++ + QString tagStyle; + + fgColor = ircColor(colorsModeRegexp.cap(1)); + bgColor = ircColor(colorsModeRegexp.cap(2)); + + toAppend = pushColorTag(fgColor, bgColor); + } + else + { + toAppend = popTag(QString::fromLatin1("span")); + } + break; + case 0x07: //System bell: ^G + KNotifyClient::beep( QString::fromLatin1("IRC beep event received in a message") ); + break; + case '\t': // 0x09 + toAppend = QString::fromLatin1(" "); + break; + case '\n': // 0x0D + toAppend= QString::fromLatin1("<br/>"); + break; + case 0x0D: // Italics: ^N + toAppend = toggleTag("i"); + break; + case 0x0F: //Plain Text, close all tags: ^O + toAppend = popAll(); + break; + // case 0x12: // Reverse original text colors: ^R + // break; + case 0x16: //Invert Colors: ^V + swap(fgColor, bgColor); + toAppend = pushColorTag(fgColor, bgColor); + break; + case 0x1F: //Underline + toAppend = toggleTag("u"); + break; + case '<': + toAppend = QString::fromLatin1("<"); + break; + case '>': + toAppend = QString::fromLatin1(">"); + break; + default: + if (cur < QChar(' ')) // search for control characters + toAppend = QString::fromLatin1("<%1>").arg(cur, 2, 16).upper(); + else + toAppend = QStyleSheet::escape(cur); + } + + chars += toAppend.length(); + buff.writeBlock( toAppend.latin1(), toAppend.length() ); + } + + QString toAppend = popAll(); + chars += toAppend.length(); + buff.writeBlock( toAppend.latin1(), toAppend.length() ); + + // Make sure we have enough room for NULL character. + if (data.size() < chars+1) + data.resize(chars+1); + + data[chars] = '\0'; + + return data; +} + +QString KSParser::pushTag(const QString &tag, const QString &attributes) +{ + QString res; + m_tags.push(tag); + if(!m_attributes.contains(tag)) + m_attributes.insert(tag, attributes); + else if(!attributes.isEmpty()) + m_attributes.replace(tag, attributes); + res.append("<" + tag); + if(!m_attributes[tag].isEmpty()) + res.append(" " + m_attributes[tag]); + return res + ">"; +} + +QString KSParser::pushColorTag(const QColor &fgColor, const QColor &bgColor) +{ + QString tagStyle; + + if (fgColor.isValid()) + tagStyle += QString::fromLatin1("color:%1;").arg(fgColor.name()); + if (bgColor.isValid()) + tagStyle += QString::fromLatin1("background-color:%1;").arg(bgColor.name()); + + if(!tagStyle.isEmpty()) + tagStyle = QString::fromLatin1("style=\"%1\"").arg(tagStyle); + + return pushTag(QString::fromLatin1("span"), tagStyle);; +} + +QString KSParser::popTag(const QString &tag) +{ + if (!m_tags.contains(tag)) + return QString::null; + + QString res; + QValueStack<QString> savedTags; + while(m_tags.top() != tag) + { + savedTags.push(m_tags.pop()); + res.append("</" + savedTags.top() + ">"); + } + res.append("</" + m_tags.pop() + ">"); + m_attributes.remove(tag); + while(!savedTags.isEmpty()) + res.append(pushTag(savedTags.pop())); + return res; +} + +QString KSParser::toggleTag(const QString &tag) +{ + return m_attributes.contains(tag)?popTag(tag):pushTag(tag); +} + +QString KSParser::popAll() +{ + QString res; + while(!m_tags.isEmpty()) + res.append("</" + m_tags.pop() + ">"); + m_attributes.clear(); + return res; +} + +QColor KSParser::ircColor(const QString &color) +{ + bool success; + unsigned int intColor = color.toUInt(&success); + + if (success) + return ircColor(intColor); + else + return QColor(); +} + +QColor KSParser::ircColor(unsigned int color) +{ + unsigned int maxcolor = sizeof(IRC_Colors)/sizeof(QColor); + return color<=maxcolor?IRC_Colors[color]:IRC_Colors[maxcolor]; +} + +int KSParser::colorForHTML(const QString &html) +{ + QColor color(html); + for(uint i=0; i<sizeof(IRC_Colors)/sizeof(QColor); i++) + { + if(IRC_Colors[i] == color) + return i; + } + return -1; +} diff --git a/kopete/protocols/irc/ksparser.h b/kopete/protocols/irc/ksparser.h new file mode 100644 index 00000000..dda7b7c1 --- /dev/null +++ b/kopete/protocols/irc/ksparser.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Simon Hausmann <hausmann@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Artistic License. +*/ +#ifndef __ksparser_h__ +#define __ksparser_h__ + +#include <qcolor.h> +#include <qmap.h> +#include <qregexp.h> +#include <qstring.h> +#include <qvaluestack.h> + +/* + * Helper class to parse IRC color/style codes and convert them to + * richtext. The parser maintains an internal stack of the styles + * applied because the IRC message could contain sequences as + * (bold)Hello (red)World(endbold)! (blue)blue text + * which needs to be converted to + * <b>Hello </b><font color="red"><b>World</b>! </font><font color="blue">blue text</font> + * to get correctly nested tags. (malte) + */ +class KSParser +{ +public: + static QCString parse(const QCString &); + static int colorForHTML( const QString &html ); + + static QColor ircColor(const QString &color); + static QColor ircColor(unsigned int color); + + ~KSParser(); +private: + KSParser(); + + QCString _parse(const QCString &); + QString pushTag(const QString &, const QString & = QString::null); + QString pushColorTag(const QColor &fgColor, const QColor &bgColor); + QString popTag(const QString &); + QString toggleTag(const QString &); + QString popAll(); + +private: + static KSParser m_parser; + static const QColor IRC_Colors[17]; + static const QRegExp sm_colorsModeRegexp; + + QValueStack<QString> m_tags; + QMap<QString, QString> m_attributes; +}; + +#endif + + diff --git a/kopete/protocols/irc/libkirc/Makefile.am b/kopete/protocols/irc/libkirc/Makefile.am new file mode 100644 index 00000000..e2ebe543 --- /dev/null +++ b/kopete/protocols/irc/libkirc/Makefile.am @@ -0,0 +1,20 @@ +KDE_OPTIONS = nofinal +noinst_LTLIBRARIES = libkirc.la + +libkirc_la_SOURCES = \ + kircengine.cpp \ + kircengine_commands.cpp \ + kircengine_ctcp.cpp \ + kircengine_numericreplies.cpp \ + kircentity.cpp \ + kircmessage.cpp \ + kircmessageredirector.cpp \ + kirctransfer.cpp \ + kirctransferhandler.cpp \ + kirctransferserver.cpp \ + ksslsocket.cpp +libkirc_la_LDFLAGS = -no-undefined $(KDE_PLUGIN) $(all_libraries) +libkirc_la_LIBADD = $(LIB_KIO) + +AM_CPPFLAGS = -I$(top_srcdir)/kopete/protocols/irc $(KOPETE_INCLUDES) $(all_includes) +METASOURCES = AUTO diff --git a/kopete/protocols/irc/libkirc/kircengine.cpp b/kopete/protocols/irc/libkirc/kircengine.cpp new file mode 100644 index 00000000..5b70d5fc --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircengine.cpp @@ -0,0 +1,497 @@ +/* + kirc.cpp - IRC Client + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + + Kopete (c) 2002-2005 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. * + * * + ************************************************************************* +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "kircengine.h" +#include "ksslsocket.h" + +#include <kconfig.h> +#include <kdebug.h> +#include <kextsock.h> +#include <klocale.h> +#include <kstandarddirs.h> + +#include <qtextcodec.h> +#include <qtimer.h> + +//Needed for getuid / getpwuid +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> + +#include <kopetemessage.h> + +#ifndef KIRC_SSL_SUPPORT +#define KIRC_SSL_SUPPORT +#endif + +using namespace KIRC; + +// FIXME: Remove slotConnected() and error(int errCode) while going to KNetwork namespace + +/* Please note that the regular expression "[\\r\\n]*$" is used in a QString::replace statement many times. + * This gets rid of trailing \r\n, \r, \n, and \n\r characters. + */ +const QRegExp Engine::m_RemoveLinefeeds( QString::fromLatin1("[\\r\\n]*$") ); + +Engine::Engine(QObject *parent, const char *name) + : QObject(parent, QString::fromLatin1("[KIRC::Engine]%1").arg(name).latin1()), + m_status(Idle), + m_FailedNickOnLogin(false), + m_useSSL(false), + m_commands(101, false), +// m_numericCommands(101), + m_ctcpQueries(17, false), + m_ctcpReplies(17, false), + codecs(577,false) +{ + setUserName(QString::null); + + m_commands.setAutoDelete(true); + m_ctcpQueries.setAutoDelete(true); + m_ctcpReplies.setAutoDelete(true); + + bindCommands(); + bindNumericReplies(); + bindCtcp(); + + m_VersionString = QString::fromLatin1("Anonymous client using the KIRC engine."); + m_UserString = QString::fromLatin1("Response not supplied by user."); + m_SourceString = QString::fromLatin1("Unknown client, known source."); + + defaultCodec = QTextCodec::codecForMib(106); // UTF8 mib is 106 + kdDebug(14120) << "Setting default engine codec, " << defaultCodec->name() << endl; + + m_sock = 0L; +} + +Engine::~Engine() +{ + kdDebug(14120) << k_funcinfo << m_Host << endl; + quit("KIRC Deleted", true); + if( m_sock ) + delete m_sock; +} + +void Engine::setUseSSL( bool useSSL ) +{ + kdDebug(14120) << k_funcinfo << useSSL << endl; + + if( !m_sock || useSSL != m_useSSL ) + { + if( m_sock ) + delete m_sock; + + m_useSSL = useSSL; + + + if( m_useSSL ) + { + #ifdef KIRC_SSL_SUPPORT + m_sock = new KSSLSocket; + m_sock->setSocketFlags( KExtendedSocket::inetSocket ); + + connect(m_sock, SIGNAL(certificateAccepted()), SLOT(slotConnected())); + connect(m_sock, SIGNAL(certificateRejected()), SLOT(slotConnectionClosed())); + connect(m_sock, SIGNAL(sslFailure()), SLOT(slotConnectionClosed())); + } + else + #else + kdWarning(14120) << "You tried to use SSL, but this version of Kopete was" + " not compiled with IRC SSL support. A normal IRC connection will be attempted." << endl; + } + #endif + { + m_sock = new KExtendedSocket; + m_sock->setSocketFlags( KExtendedSocket::inputBufferedSocket | KExtendedSocket::inetSocket ); + + connect(m_sock, SIGNAL(connectionSuccess()), SLOT(slotConnected())); + connect(m_sock, SIGNAL(connectionFailed(int)), SLOT(error(int))); + } + + connect(m_sock, SIGNAL(closed(int)), SLOT(slotConnectionClosed())); + connect(m_sock, SIGNAL(readyRead()), SLOT(slotReadyRead())); + } +} + +void Engine::setStatus(Engine::Status status) +{ + kdDebug(14120) << k_funcinfo << status << endl; + + if (m_status == status) + return; + +// Engine::Status oldStatus = m_status; + m_status = status; + emit statusChanged(status); + + switch (m_status) + { + case Idle: + // Do nothing. + break; + case Connecting: + // Do nothing. + break; + case Authentifying: + m_sock->enableRead(true); + + // If password is given for this server, send it now, and don't expect a reply + if (!(password()).isEmpty()) + pass(password()); + + user(m_Username, 0, m_realName); + nick(m_Nickname); + + break; + case Connected: + // Do nothing. + break; + case Closing: + m_sock->close(); + m_sock->reset(); + setStatus(Idle); + break; + case AuthentifyingFailed: + setStatus(Closing); + break; + case Timeout: + setStatus(Closing); + break; + case Disconnected: + setStatus(Closing); + break; + } +} + +void Engine::connectToServer(const QString &host, Q_UINT16 port, const QString &nickname, bool useSSL ) +{ + setUseSSL(useSSL); + + m_Nickname = nickname; + m_Host = host; + m_Port = port; + + kdDebug(14120) << "Trying to connect to server " << m_Host << ":" << m_Port << endl; + kdDebug(14120) << "Sock status: " << m_sock->socketStatus() << endl; + + if( !m_sock->setAddress(m_Host, m_Port) ) + kdDebug(14120) << k_funcinfo << "setAddress failed. Status: " << m_sock->socketStatus() << endl; + + if( m_sock->startAsyncConnect() == 0 ) + { + kdDebug(14120) << k_funcinfo << "Success!. Status: " << m_sock->socketStatus() << endl; + setStatus(Connecting); + } + else + { + kdDebug(14120) << k_funcinfo << "Failed. Status: " << m_sock->socketStatus() << endl; + setStatus(Disconnected); + } +} + +void Engine::slotConnected() +{ + setStatus(Authentifying); +} + +void Engine::slotConnectionClosed() +{ + setStatus(Disconnected); +} + +void Engine::error(int errCode) +{ + kdDebug(14120) << k_funcinfo << "Socket error: " << errCode << endl; + if (m_sock->socketStatus () != KExtendedSocket::connecting) + { + // Connection in progress.. This is a signal fired wrong + setStatus(Disconnected); + } +} + +void Engine::setVersionString(const QString &newString) +{ + m_VersionString = newString; + m_VersionString.remove(m_RemoveLinefeeds); +} + +void Engine::setUserString(const QString &newString) +{ + m_UserString = newString; + m_UserString.remove(m_RemoveLinefeeds); +} + +void Engine::setSourceString(const QString &newString) +{ + m_SourceString = newString; + m_SourceString.remove(m_RemoveLinefeeds); +} + +void Engine::setUserName(const QString &newName) +{ + if(newName.isEmpty()) + m_Username = QString::fromLatin1(getpwuid(getuid())->pw_name); + else + m_Username = newName; + m_Username.remove(m_RemoveLinefeeds); +} + +void Engine::setRealName(const QString &newName) +{ + if(newName.isEmpty()) + m_realName = QString::fromLatin1(getpwuid(getuid())->pw_gecos); + else + m_realName = newName; + m_realName.remove(m_RemoveLinefeeds); +} + +bool Engine::_bind(QDict<KIRC::MessageRedirector> &dict, + QString command, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage) +{ +// FIXME: Force upper case. +// FIXME: Force number format. + + MessageRedirector *mr = dict[command]; + + if (!mr) + { + mr = new MessageRedirector(this, minArgs, maxArgs, helpMessage); + dict.replace(command, mr); + } + + return mr->connect(object, member); +} + +bool Engine::bind(const QString &command, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage) +{ + return _bind(m_commands, command, object, member, + minArgs, maxArgs, helpMessage); +} + +bool Engine::bind(int id, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage) +{ + return _bind(m_commands, QString::number(id), object, member, + minArgs, maxArgs, helpMessage); +} + +bool Engine::bindCtcpQuery(const QString &command, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage) +{ + return _bind(m_ctcpQueries, command, object, member, + minArgs, maxArgs, helpMessage); +} + +bool Engine::bindCtcpReply(const QString &command, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage) +{ + return _bind(m_ctcpReplies, command, object, member, + minArgs, maxArgs, helpMessage); +} + +/* Message will be send as passed. + */ +void Engine::writeRawMessage(const QString &rawMsg) +{ + Message::writeRawMessage(this, defaultCodec, rawMsg); +} + +/* Message will be quoted before beeing send. + */ +void Engine::writeMessage(const QString &msg, const QTextCodec *codec) +{ + Message::writeMessage(this, codec ? codec : defaultCodec, msg); +} + +void Engine::writeMessage(const QString &command, const QStringList &args, const QString &suffix, const QTextCodec *codec) +{ + Message::writeMessage(this, codec ? codec : defaultCodec, command, args, suffix ); +} + +void Engine::writeCtcpMessage(const QString &command, const QString &to, const QString &ctcpMessage) +{ + Message::writeCtcpMessage(this, defaultCodec, command, to, ctcpMessage); +} + +void Engine::writeCtcpMessage(const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix, bool ) +{ + QString nick = Entity::userNick(to); + + Message::writeCtcpMessage(this, codecForNick( nick ), command, nick, suffix, + ctcpCommand, ctcpArgs, ctcpSuffix ); +} + +void Engine::slotReadyRead() +{ + // This condition is buggy when the peer server + // close the socket unexpectedly + bool parseSuccess; + + if (m_sock->socketStatus() == KExtendedSocket::connected && m_sock->canReadLine()) + { + Message msg = Message::parse(this, defaultCodec, &parseSuccess); + if (parseSuccess) + { + emit receivedMessage(msg); + + KIRC::MessageRedirector *mr; + if (msg.isNumeric()) +// mr = m_numericCommands[ msg.command().toInt() ]; + // we do this conversion because some dummy servers sends 1 instead of 001 + // numbers are stored as "1" instead of "001" to make convertion faster (no 0 pading). + mr = m_commands[ QString::number(msg.command().toInt()) ]; + else + mr = m_commands[ msg.command() ]; + + if (mr) + { + QStringList errors = mr->operator()(msg); + + if (!errors.isEmpty()) + { + kdDebug(14120) << "Method error for line:" << msg.raw() << endl; + emit internalError(MethodFailed, msg); + } + } + else if (msg.isNumeric()) + { + kdWarning(14120) << "Unknown IRC numeric reply for line:" << msg.raw() << endl; + emit incomingUnknown(msg.raw()); + } + else + { + kdWarning(14120) << "Unknown IRC command for line:" << msg.raw() << endl; + emit internalError(UnknownCommand, msg); + } + } + else + { + emit incomingUnknown(msg.raw()); + emit internalError(ParsingFailed, msg); + } + + QTimer::singleShot( 0, this, SLOT( slotReadyRead() ) ); + } + + if(m_sock->socketStatus() != KExtendedSocket::connected) + error(); +} + +const QTextCodec *Engine::codecForNick( const QString &nick ) const +{ + if( nick.isEmpty() ) + return defaultCodec; + + QTextCodec *codec = codecs[ nick ]; + kdDebug(14120) << nick << " has codec " << codec << endl; + + if( !codec ) + return defaultCodec; + else + return codec; +} + +void Engine::showInfoDialog() +{ + if( m_useSSL ) + { + static_cast<KSSLSocket*>( m_sock )->showInfoDialog(); + } +} + +/* + * The ctcp commands seems to follow the same message behaviours has normal IRC command. + * (Only missing the \n\r final characters) + * So applying the same parsing rules to the messages. + */ +bool Engine::invokeCtcpCommandOfMessage(const QDict<MessageRedirector> &map, Message &msg) +{ + if(msg.hasCtcpMessage() && msg.ctcpMessage().isValid()) + { + Message &ctcpMsg = msg.ctcpMessage(); + + MessageRedirector *mr = map[ctcpMsg.command()]; + if (mr) + { + QStringList errors = mr->operator()(msg); + + if (errors.isEmpty()) + return true; + + kdDebug(14120) << "Method error for line:" << ctcpMsg.raw() << endl; + writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(), + QString::fromLatin1("%1 internal error(s)").arg(errors.size())); + } + else + { + kdDebug(14120) << "Unknow IRC/CTCP command for line:" << ctcpMsg.raw() << endl; + // Don't send error message on unknown CTCP command + // None of the client send it, and it makes the client as infected by virus for IRC network scanners + // writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(), "Unknown CTCP command"); + + emit incomingUnknownCtcp(msg.ctcpRaw()); + } + } + else + { + kdDebug(14120) << "Message do not embed a CTCP message:" << msg.raw(); + } + return false; +} + +EntityPtr Engine::getEntity(const QString &name) +{ + Entity *entity = 0; + + #pragma warning Do the searching code here. + + if (!entity) + { + entity = new Entity(name); + m_entities.append(entity); + } + + connect(entity, SIGNAL(destroyed(KIRC::Entity *)), SLOT(destroyed(KIRC::Entity *))); + return EntityPtr(entity); +} + +void Engine::destroyed(KIRC::Entity *entity) +{ + m_entities.remove(entity); +} + +void Engine::ignoreMessage(KIRC::Message &/*msg*/) +{ +} + +void Engine::emitSuffix(KIRC::Message &msg) +{ + emit receivedMessage(InfoMessage, m_server, m_server, msg.suffix()); +} + +#include "kircengine.moc" + +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/irc/libkirc/kircengine.h b/kopete/protocols/irc/libkirc/kircengine.h new file mode 100644 index 00000000..50cb8f49 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircengine.h @@ -0,0 +1,532 @@ +/* + kircengine.h - IRC Client + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + 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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCENGINE_H +#define KIRCENGINE_H + +#include "kircentity.h" +#include "kircmessage.h" +#include "kircmessageredirector.h" +#include "kirctransfer.h" + +#include <kdeversion.h> + +// FIXME: Move the following kdedebug class to the *.cpp. +#include <kdebug.h> +#if KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 ) +#include <kdebugclasses.h> +#endif + +#include <qdatetime.h> +#include <qdict.h> +#include <qintdict.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> + +class QRegExp; + +namespace KIRC +{ + +/** + * @author Nick Betcher <nbetcher@kde.org> + * @author Michel Hermier <michel.hermier@wanadoo.fr> + * @author Jason Keirstead <jason@keirstead.org> + */ +class Engine + : public QObject +{ + Q_OBJECT + +// Q_PROPERTY(QUrl serverURL READ serverURL WRITE setServerURL) + +// Extracted from the base of the serverURL. +// Q_PROPERTY(bool useSSL); +// Q_PROPERTY(QString user READ user); +// Q_PROPERTY(QString password); +// Q_PROPERTY(QString host READ host); +// Q_PROPERTY(int port READ host); + +// Extracted from the query of the serverURL. +// Q_PROPERTY(bool reqsPasswd); +// Q_PROPERTY(QString name); // real name +// Q_PROPERTY(QStringList nickList READ nickList WRITE setNickList) +// Q_PROPERTY(QString nick READ nick) +// Q_PROPERTY(QStringList portList) + + Q_ENUMS(Status) + +public: + enum Error + { + ParsingFailed, + UnknownCommand, + UnknownNumericReply, + InvalidNumberOfArguments, + MethodFailed + }; + + enum Status + { + Idle, + Connecting, + Authentifying, + Connected, + Closing, + AuthentifyingFailed, + Timeout, + Disconnected + }; + + enum ServerMessageType + { + ErrorMessage = -1, + PrivateMessage, + InfoMessage, + + MessageOfTheDayMessage, + MessageOfTheDayCondensedMessage + }; + + Engine( QObject *parent = 0, const char* name = 0 ); + ~Engine(); + +// QString nick() const; +// QStringList nickList() const; +// void setNickList(const QStringList& nickList); + +// QUrl serverURL() const; +// bool setServerURL(const QUrl &url); + + inline const QString ¤tHost() const + { return m_Host; }; + + inline Q_UINT16 currentPort() + { return m_Port; } + + inline const QString &nickName() const + { return m_Nickname; }; + + inline const QString &password() const + { return m_Passwd; } + + inline void setPassword(const QString &passwd) + { m_Passwd = passwd; }; + + inline const QString &userName() const + { return m_Username; } + + void setUserName(const QString &newName); + + void setRealName(const QString &newName); + inline const QString &realName() const + { return m_realName; } + + inline const bool reqsPassword() const + { return m_ReqsPasswd; }; + + inline void setReqsPassword(bool b) + { m_ReqsPasswd = b; }; + + const bool useSSL() const { return m_useSSL; }; + void setUseSSL( bool useSSL ); + + inline const QTextCodec *codec() const + { return defaultCodec; }; + + const QTextCodec *codecForNick( const QString &nick ) const; + + inline void setDefaultCodec( QTextCodec* codec ) + { defaultCodec = codec; }; + + void setVersionString(const QString &versionString); + void setUserString(const QString &userString); + void setSourceString(const QString &sourceString); + void connectToServer(const QString &host, Q_UINT16 port, const QString &nickname, bool useSSL = false); + + KExtendedSocket *socket() + { return m_sock; }; + + inline KIRC::Engine::Status status() const + { return m_status; } + + inline bool isDisconnected() const + { return m_status == Disconnected || m_status == Idle; } + + inline bool isConnected() const + { return m_status == Connected; } + + inline void setCodec( const QString &nick, const QTextCodec *codec ) + { codecs.replace( nick, codec ); } + + /* Custom CTCP replies handling */ + inline QString &customCtcp( const QString &s ) + { return customCtcpMap[s]; } + + inline void addCustomCtcp( const QString &ctcp, const QString &reply ) + { customCtcpMap[ ctcp.lower() ] = reply; } + + KIRC::EntityPtr getEntity(const QString &name); + +public slots: + //Message output + void writeRawMessage(const QString &message); + + void writeMessage(const QString &message, const QTextCodec *codec = 0 ); + void writeMessage(const QString &command, const QStringList &args, + const QString &suffix = QString::null, const QTextCodec *codec = 0); + + void writeCtcpMessage(const QString &command, const QString &to, const QString &ctcpMessage); + + void writeCtcpMessage(const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true); + + inline void writeCtcpQueryMessage(const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true) + { return writeCtcpMessage("PRIVMSG", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); } + + inline void writeCtcpReplyMessage(const QString &to, const QString &ctcpMessage) + { writeCtcpMessage("NOTICE", to, ctcpMessage); } + + inline void writeCtcpReplyMessage(const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true) + { return writeCtcpMessage("NOTICE", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); } + + inline void writeCtcpErrorMessage(const QString &to, const QString &ctcpLine, const QString &errorMsg, + bool emitRepliedCtcp=true) + { return writeCtcpReplyMessage(to, QString::null, "ERRMSG", ctcpLine, errorMsg, emitRepliedCtcp); } + + bool bind(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bind(int id, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bindCtcpQuery(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bindCtcpReply(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + + void away(bool isAway, const QString &awayMessage = QString::null); + void ison(const QStringList &nickList); + void join(const QString &name, const QString &key); + void kick(const QString &user, const QString &channel, const QString &reason); + void list(); + void mode(const QString &target, const QString &mode); + void motd(const QString &server = QString::null); + void nick(const QString &newNickname); + void notice(const QString &target, const QString &message); + void part(const QString &name, const QString &reason); + void pass(const QString &password); + void privmsg(const QString &contact, const QString &message); + + /** + * Send a quit message for the given reason. + * If now is set to true the connection is closed and no event message is sent. + * Therefore setting now to true should only be used while destroying the object. + */ + void quit(const QString &reason, bool now=false); + + void topic(const QString &channel, const QString &topic); + void user(const QString &newUsername, const QString &hostname, const QString &newRealname); + void user(const QString &newUsername, Q_UINT8 mode, const QString &newRealname); + void whois(const QString &user); + + + /* CTCP commands */ + void CtcpRequestCommand(const QString &contact, const QString &command); + void CtcpRequest_action(const QString &contact, const QString &message); + void CtcpRequest_dcc(const QString &, const QString &, unsigned int port, KIRC::Transfer::Type type); + void CtcpRequest_ping(const QString &target); + void CtcpRequest_version(const QString &target); + +public slots: + void showInfoDialog(); + +signals: + void statusChanged(KIRC::Engine::Status newStatus); + void internalError(KIRC::Engine::Error, KIRC::Message &); + + void receivedMessage(KIRC::Message &); + + /** + * Emit a received message. + * The received message could have been translated to your locale. + * + * @param type the message type. + * @param from the originator of the message. + * @param to is the list of entities that are related to this message. + * @param msg the message (usually translated). + * + * @note Most of the following numeric messages should be deprecated, and call this method instead. + * Most of the methods, using it, update KIRC::Entities. + * Lists based messages are sent via dedicated API, therefore they don't use this. + */ + // @param args the args to apply to this message. + void receivedMessage( KIRC::Engine::ServerMessageType type, + const KIRC::EntityPtr &from, + const KIRC::EntityPtrList &to, + const QString &msg); + + void successfullyChangedNick(const QString &, const QString &); + + //ServerContact Signals + void incomingMotd(const QString &motd); + void incomingNotice(const QString &originating, const QString &message); + void incomingHostInfo(const QString &servername, const QString &version, + const QString &userModes, const QString &channelModes); + void incomingYourHostInfo(const QString &servername, const QString &version, + const QString &userModes, const QString &channelModes); + void incomingConnectString(const QString &clients); + + //Channel Contact Signals + void incomingMessage(const QString &originating, const QString &target, const QString &message); + void incomingTopicChange(const QString &, const QString &, const QString &); + void incomingExistingTopic(const QString &, const QString &); + void incomingTopicUser(const QString &channel, const QString &user, const QDateTime &time); + void incomingJoinedChannel(const QString &channel,const QString &nick); + void incomingPartedChannel(const QString &channel,const QString &nick, const QString &reason); + void incomingNamesList(const QString &channel, const QStringList &nicknames); + void incomingEndOfNames(const QString &channel); + void incomingChannelMode(const QString &channel, const QString &mode, const QString ¶ms); + void incomingCannotSendToChannel(const QString &channel, const QString &message); + void incomingChannelModeChange(const QString &channel, const QString &nick, const QString &mode); + void incomingChannelHomePage(const QString &channel, const QString &url); + + //Contact Signals + void incomingPrivMessage(const QString &, const QString &, const QString &); + void incomingQuitIRC(const QString &user, const QString &reason); + void incomingUserModeChange(const QString &nick, const QString &mode); + void incomingNoSuchNickname(const QString &nick); + + // CTCP Signals +// void action(const QString &from, const QString &to, const QString &message); + void incomingAction(const QString &channel, const QString &originating, const QString &message); + void incomingPrivAction(const QString &target, const QString &originating, const QString &message); + + //Response Signals + void incomingUserOnline(const QString &nick); + void incomingWhoIsUser(const QString &nickname, const QString &username, + const QString &hostname, const QString &realname); + void incomingWhoWasUser(const QString &nickname, const QString &username, + const QString &hostname, const QString &realname); + void incomingWhoIsServer(const QString &nickname, const QString &server, const QString &serverInfo); + void incomingWhoIsOperator(const QString &nickname); + void incomingWhoIsIdentified(const QString &nickname); + void incomingWhoIsChannels(const QString &nickname, const QString &channel); + void incomingWhoIsIdle(const QString &nickname, unsigned long seconds); /* 317 */ + void incomingSignOnTime(const QString &nickname, unsigned long seconds); /* 317 */ + void incomingEndOfWhois(const QString &nickname); + void incomingEndOfWhoWas(const QString &nickname); + + void incomingWhoReply( const QString &nick, const QString &channel, const QString &user, const QString &host, + const QString &server,bool away, const QString &flag, uint hops, const QString &realName ); + + void incomingEndOfWho( const QString &query ); + + //Error Message Signals + void incomingServerLoadTooHigh(); + void incomingNickInUse(const QString &usingNick); + void incomingNickChange(const QString &, const QString &); + void incomingFailedServerPassword(); + void incomingFailedChankey(const QString &); + void incomingFailedChanBanned(const QString &); + void incomingFailedChanInvite(const QString &); + void incomingFailedChanFull(const QString &); + void incomingFailedNickOnLogin(const QString &); + void incomingNoNickChan(const QString &); + void incomingWasNoNick(const QString &); + + //General Signals + void incomingUnknown(const QString &); + void incomingUnknownCtcp(const QString &); + void incomingKick(const QString &channel, const QString &nick, + const QString &nickKicked, const QString &reason); + + void incomingUserIsAway(const QString &nick, const QString &awayMessage); + void incomingListedChan(const QString &chan, uint users, const QString &topic); + void incomingEndOfList(); + + void incomingCtcpReply(const QString &type, const QString &target, const QString &messageReceived); + +private slots: + void destroyed(KIRC::Entity *entity); + + void slotReadyRead(); + + void slotConnected(); + void slotConnectionClosed(); + void error(int errCode = 0); + + void ignoreMessage(KIRC::Message &msg); + void emitSuffix(KIRC::Message &); + + void error(KIRC::Message &msg); + void join(KIRC::Message &msg); + void kick(KIRC::Message &msg); + void mode(KIRC::Message &msg); + void nick(KIRC::Message &msg); + void notice(KIRC::Message &msg); + void part(KIRC::Message &msg); + void ping(KIRC::Message &msg); + void pong(KIRC::Message &msg); + void privmsg(KIRC::Message &msg); +// void squit(KIRC::Message &msg); + void quit(KIRC::Message &msg); + void topic(KIRC::Message &msg); + + void numericReply_001(KIRC::Message &msg); + void numericReply_002(KIRC::Message &msg); + void numericReply_003(KIRC::Message &msg); + void numericReply_004(KIRC::Message &msg); + void numericReply_005(KIRC::Message &msg); + void numericReply_250(KIRC::Message &msg); + void numericReply_251(KIRC::Message &msg); + void numericReply_252(KIRC::Message &msg); + void numericReply_253(KIRC::Message &msg); + void numericReply_254(KIRC::Message &msg); + void numericReply_255(KIRC::Message &msg); + void numericReply_263(KIRC::Message &msg); + void numericReply_265(KIRC::Message &msg); + void numericReply_266(KIRC::Message &msg); + void numericReply_301(KIRC::Message &msg); + void numericReply_303(KIRC::Message &msg); +// void numericReply_305(KIRC::Message &msg); +// void numericReply_306(KIRC::Message &msg); + void numericReply_307(KIRC::Message &msg); + void numericReply_311(KIRC::Message &msg); + void numericReply_312(KIRC::Message &msg); + void numericReply_313(KIRC::Message &msg); + void numericReply_314(KIRC::Message &msg); + void numericReply_315(KIRC::Message &msg); + void numericReply_317(KIRC::Message &msg); + void numericReply_318(KIRC::Message &msg); + void numericReply_319(KIRC::Message &msg); + void numericReply_320(KIRC::Message &msg); + void numericReply_322(KIRC::Message &msg); + void numericReply_323(KIRC::Message &msg); + void numericReply_324(KIRC::Message &msg); + void numericReply_328(KIRC::Message &msg); + void numericReply_329(KIRC::Message &msg); + void numericReply_331(KIRC::Message &msg); + void numericReply_332(KIRC::Message &msg); + void numericReply_333(KIRC::Message &msg); + void numericReply_352(KIRC::Message &msg); + void numericReply_353(KIRC::Message &msg); + void numericReply_366(KIRC::Message &msg); + void numericReply_369(KIRC::Message &msg); + void numericReply_372(KIRC::Message &msg); +// void numericReply_376(KIRC::Message &msg); + + void numericReply_401(KIRC::Message &msg); + void numericReply_406(KIRC::Message &msg); + void numericReply_422(KIRC::Message &msg); + void numericReply_433(KIRC::Message &msg); + void numericReply_464(KIRC::Message &msg); + void numericReply_471(KIRC::Message &msg); + void numericReply_473(KIRC::Message &msg); + void numericReply_474(KIRC::Message &msg); + void numericReply_475(KIRC::Message &msg); + + + void CtcpQuery_action(KIRC::Message &msg); + void CtcpQuery_clientinfo(KIRC::Message &msg); + void CtcpQuery_finger(KIRC::Message &msg); + void CtcpQuery_dcc(KIRC::Message &msg); + void CtcpQuery_ping(KIRC::Message &msg); + void CtcpQuery_source(KIRC::Message &msg); + void CtcpQuery_time(KIRC::Message &msg); + void CtcpQuery_userinfo(KIRC::Message &msg); + void CtcpQuery_version(KIRC::Message &msg); + + void CtcpReply_errmsg(KIRC::Message &msg); + void CtcpReply_ping(KIRC::Message &msg); + void CtcpReply_version(KIRC::Message &msg); + +private: + void bindCommands(); + void bindNumericReplies(); + void bindCtcp(); + + void setStatus(KIRC::Engine::Status status); + bool invokeCtcpCommandOfMessage(const QDict<KIRC::MessageRedirector> &map, KIRC::Message &message); + + /* + * Methods that handles all the bindings creations. + * This methods is used by all the bind(...) methods. + */ + bool _bind(QDict<KIRC::MessageRedirector> &dict, + QString command, QObject *object, const char *member, + int minArgs, int maxArgs, const QString &helpMessage); + + //Static regexes + static const QRegExp m_RemoveLinefeeds; + + KIRC::Engine::Status m_status; + QString m_Host; + Q_UINT16 m_Port; + +// QUrl serverURL; +// QUrl currentServerURL; + QString m_Nickname; + QString m_Username; + QString m_realName; + QString m_Passwd; + bool m_ReqsPasswd; + bool m_FailedNickOnLogin; + bool m_useSSL; + + QValueList<KIRC::Entity *> m_entities; + KIRC::EntityPtr m_server; + KIRC::EntityPtr m_self; + + QString m_VersionString; + QString m_UserString; + QString m_SourceString; + QString m_PendingNick; + + QDict<KIRC::MessageRedirector> m_commands; +// QIntDict<KIRC::MessageRedirector> m_numericCommands; + QDict<KIRC::MessageRedirector> m_ctcpQueries; + QDict<KIRC::MessageRedirector> m_ctcpReplies; + + QMap<QString, QString> customCtcpMap; + QDict<QTextCodec> codecs; + QTextCodec *defaultCodec; + + KExtendedSocket *m_sock; +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/kircengine_commands.cpp b/kopete/protocols/irc/libkirc/kircengine_commands.cpp new file mode 100644 index 00000000..0a0f9002 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircengine_commands.cpp @@ -0,0 +1,312 @@ +/* + kirc_commands.h - IRC Client + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2003 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 "kircengine.h" + +#include <kextsock.h> + +#include <qtimer.h> + +using namespace KIRC; + +void Engine::bindCommands() +{ + bind("ERROR", this, SLOT(error(KIRC::Message &)), 0, 0); + bind("JOIN", this, SLOT(join(KIRC::Message &)), 0, 1); + bind("KICK", this, SLOT(kick(KIRC::Message &)), 2, 2); + bind("NICK", this, SLOT(nick(KIRC::Message &)), 0, 0); + bind("MODE", this, SLOT(mode(KIRC::Message &)), 1, 1); + bind("NOTICE", this, SLOT(notice(KIRC::Message &)), 1, 1); + bind("PART", this, SLOT(part(KIRC::Message &)), 1, 1); + bind("PING", this, SLOT(ping(KIRC::Message &)), 0, 0); + bind("PONG", this, SLOT(pong(KIRC::Message &)), 0, 0); + bind("PRIVMSG", this, SLOT(privmsg(KIRC::Message &)), 1, 1); + bind("QUIT", this, SLOT(quit(KIRC::Message &)), 0, 0); +// bind("SQUIT", this, SLOT(squit(KIRC::Message &)), 1, 1); + bind("TOPIC", this, SLOT(topic(KIRC::Message &)), 1, 1); +} + +void Engine::away(bool isAway, const QString &awayMessage) +{ + if(isAway) + if( !awayMessage.isEmpty() ) + writeMessage("AWAY", QString::null, awayMessage); + else + writeMessage("AWAY", QString::null, QString::fromLatin1("I'm away.")); + else + writeMessage("AWAY", QString::null); +} + +// FIXME: Really handle this message +void Engine::error(Message &) +{ + setStatus(Closing); +} + +void Engine::ison(const QStringList &nickList) +{ + if (!nickList.isEmpty()) + { + QString statement = QString::fromLatin1("ISON"); + for (QStringList::ConstIterator it = nickList.begin(); it != nickList.end(); ++it) + { + if ((statement.length()+(*it).length())>509) // 512(max buf)-2("\r\n")-1(<space separator>) + { + writeMessage(statement); + statement = QString::fromLatin1("ISON ") + (*it); + } + else + statement.append(QChar(' ') + (*it)); + } + writeMessage(statement); + } +} + +void Engine::join(const QString &name, const QString &key) +{ + QStringList args(name); + if ( !key.isNull() ) + args << key; + + writeMessage("JOIN", args); +} + +void Engine::join(Message &msg) +{ + /* RFC say: "( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0"" + * suspected: ":<channel> *(" "/"," <channel>)" + * assumed ":<channel>" + * This is the response of someone joining a channel. + * Remember that this will be emitted when *you* /join a room for the first time */ + + if (msg.argsSize()==1) + emit incomingJoinedChannel(Kopete::Message::unescape(msg.arg(0)), msg.nickFromPrefix()); + else + emit incomingJoinedChannel(Kopete::Message::unescape(msg.suffix()), msg.nickFromPrefix()); +} + +void Engine::kick(const QString &user, const QString &channel, const QString &reason) +{ + writeMessage("KICK", QStringList(channel) << user << reason); +} + +void Engine::kick(Message &msg) +{ + /* The given user is kicked. + * "<channel> *( "," <channel> ) <user> *( "," <user> ) [<comment>]" + */ + emit incomingKick( Kopete::Message::unescape(msg.arg(0)), msg.nickFromPrefix(), msg.arg(1), msg.suffix()); +} + +void Engine::mode(const QString &target, const QString &mode) +{ + writeMessage("MODE", QStringList(target) << mode); +} + +void Engine::mode(Message &msg) +{ + /* Change the mode of a user. + * "<nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )" + */ + QStringList args = msg.args(); + args.pop_front(); + if( Entity::isChannel( msg.arg(0) ) ) + emit incomingChannelModeChange( msg.arg(0), msg.nickFromPrefix(), args.join(" ")); + else + emit incomingUserModeChange( msg.nickFromPrefix(), args.join(" ")); +} + +void Engine::nick(const QString &newNickname) +{ + m_PendingNick = newNickname; + writeMessage("NICK", newNickname); +} + +void Engine::nick(Message &msg) +{ + /* Nick name of a user changed + * "<nickname>" */ + QString oldNick = msg.prefix().section('!', 0, 0); + QString newNick = msg.suffix(); + + if( codecs[ oldNick ] ) + { + QTextCodec *c = codecs[ oldNick ]; + codecs.remove( oldNick ); + codecs.insert( newNick, c ); + } + + if (oldNick.lower() == m_Nickname.lower()) + { + emit successfullyChangedNick(oldNick, msg.suffix()); + m_Nickname = msg.suffix(); + } + else + emit incomingNickChange(oldNick, msg.suffix()); +} + +void Engine::part(const QString &channel, const QString &reason) +{ + /* This will part a channel with 'reason' as the reason for parting + */ + writeMessage("PART", channel, reason); +} + +void Engine::part(Message &msg) +{ + /* This signal emits when a user parts a channel + * "<channel> *( "," <channel> ) [ <Part Message> ]" + */ + kdDebug(14120) << "User parting" << endl; + emit incomingPartedChannel(msg.arg(0), msg.nickFromPrefix(), msg.suffix()); +} + +void Engine::pass(const QString &password) +{ + writeMessage("PASS", password); +} + +void Engine::ping(Message &msg) +{ + writeMessage("PONG", msg.arg(0), msg.suffix()); +} + +void Engine::pong(Message &/*msg*/) +{ +} + +void Engine::quit(const QString &reason, bool /*now*/) +{ + kdDebug(14120) << k_funcinfo << reason << endl; + + if (isDisconnected()) + return; + + if (isConnected()) + writeMessage("QUIT", QString::null, reason); + + setStatus(Closing); +} + +void Engine::quit(Message &msg) +{ + /* This signal emits when a user quits irc. + */ + kdDebug(14120) << "User quiting" << endl; + emit incomingQuitIRC(msg.prefix(), msg.suffix()); +} + +void Engine::user(const QString &newUserName, const QString &hostname, const QString &newRealName) +{ + /* RFC1459: "<username> <hostname> <servername> <realname>" + * The USER command is used at the beginning of connection to specify + * the username, hostname and realname of a new user. + * hostname is usualy set to "127.0.0.1" */ + m_Username = newUserName; + m_realName = newRealName; + + writeMessage("USER", QStringList(m_Username) << hostname << m_Host, m_realName); +} + +void Engine::user(const QString &newUserName, Q_UINT8 mode, const QString &newRealName) +{ + /* RFC2812: "<user> <mode> <unused> <realname>" + * mode is a numeric value (from a bit mask). + * 0x00 normal + * 0x04 request +w + * 0x08 request +i */ + m_Username = newUserName; + m_realName = newRealName; + + writeMessage("USER", QStringList(m_Username) << QString::number(mode) << QChar('*'), m_realName); +} + +void Engine::topic(const QString &channel, const QString &topic) +{ + writeMessage("TOPIC", channel, topic); +} + +void Engine::topic(Message &msg) +{ + /* The topic of a channel changed. emit the channel, new topic, and the person who changed it. + * "<channel> [ <topic> ]" + */ + emit incomingTopicChange(msg.arg(0), msg.nickFromPrefix(), msg.suffix()); +} + +void Engine::list() +{ + writeMessage("LIST", QString::null); +} + +void Engine::motd(const QString &server) +{ + writeMessage("MOTD", server); +} + +void Engine::privmsg(const QString &contact, const QString &message) +{ + writeMessage("PRIVMSG", contact, message, codecForNick( contact ) ); +} + +void Engine::privmsg(Message &msg) +{ + /* This is a signal that indicates there is a new message. + * This can be either from a channel or from a specific user. */ + Message m = msg; + if (!m.suffix().isEmpty()) + { + QString user = m.arg(0); + QString message = m.suffix(); + const QTextCodec *codec = codecForNick( user ); + if (codec != defaultCodec) { + m.decodeAgain( codec ); + message = m.suffix(); + } + if (Entity::isChannel(user)) + emit incomingMessage(m.nickFromPrefix(), Kopete::Message::unescape(m.arg(0)), message ); + else + emit incomingPrivMessage(m.nickFromPrefix(), Kopete::Message::unescape(m.arg(0)), message ); +// emit receivedMessage(PrivateMessage, msg.entityFrom(), msg.entityTo(), message); + } + + if( m.hasCtcpMessage() ) + { + invokeCtcpCommandOfMessage(m_ctcpQueries, m); + } +} + +void Engine::notice(const QString &target, const QString &message) +{ + writeMessage("NOTICE", target, message); +} + +void Engine::notice(Message &msg) +{ + if(!msg.suffix().isEmpty()) + emit incomingNotice(msg.prefix(), msg.suffix()); + + if(msg.hasCtcpMessage()) + invokeCtcpCommandOfMessage(m_ctcpReplies, msg); +} + +void Engine::whois(const QString &user) +{ + writeMessage("WHOIS", user); +} diff --git a/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp b/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp new file mode 100644 index 00000000..db1903f3 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp @@ -0,0 +1,351 @@ +/* + kirc_ctcp.h - IRC Client + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2003 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 "config.h" + +#include "kircengine.h" +#include "kirctransferhandler.h" +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <netinet/in.h> +#include <arpa/inet.h> +#include <kextsock.h> + +#include <qfileinfo.h> +#include <qregexp.h> + +using namespace KIRC; + +void Engine::bindCtcp() +{ + bindCtcpQuery("ACTION", this, SLOT(CtcpQuery_action(KIRC::Message &)), + -1, -1); + bindCtcpQuery("CLIENTINFO", this, SLOT(CtcpQuery_clientinfo(KIRC::Message &)), + -1, 1); + bindCtcpQuery("DCC", this, SLOT(CtcpQuery_dcc(KIRC::Message &)), + 4, 5); + bindCtcpQuery("FINGER", this, SLOT(CtcpQuery_finger(KIRC::Message &)), + -1, 0); + bindCtcpQuery("PING", this, SLOT(CtcpQuery_ping(KIRC::Message &)), + 1, 1); + bindCtcpQuery("SOURCE", this, SLOT(CtcpQuery_source(KIRC::Message &)), + -1, 0); + bindCtcpQuery("TIME", this, SLOT(CtcpQuery_time(KIRC::Message &)), + -1, 0); + bindCtcpQuery("USERINFO", this, SLOT(CtcpQuery_userinfo(KIRC::Message &)), + -1, 0); + bindCtcpQuery("VERSION", this, SLOT(CtcpQuery_version(KIRC::Message &)), + -1, 0); + + bindCtcpReply("ERRMSG", this, SLOT(CtcpReply_errmsg(KIRC::Message &)), + 1, -1); + bindCtcpReply("PING", this, SLOT(CtcpReply_ping(KIRC::Message &)), + 1, 1, ""); + bindCtcpReply("VERSION", this, SLOT(CtcpReply_version(KIRC::Message &)), + -1, -1, ""); +} + +// Normal order for a ctcp command: +// CtcpRequest_* +// CtcpQuery_* +// CtcpReply_* (if any) + +/* Generic ctcp commnd for the /ctcp trigger */ +void Engine::CtcpRequestCommand(const QString &contact, const QString &command) +{ + if(m_status == Connected) + { + writeCtcpQueryMessage(contact, QString::null, command); +// emit ctcpCommandMessage( contact, command ); + } +} + +void Engine::CtcpRequest_action(const QString &contact, const QString &message) +{ + if(m_status == Connected) + { + writeCtcpQueryMessage(contact, QString::null, "ACTION", message); + + if( Entity::isChannel(contact) ) + emit incomingAction(Kopete::Message::unescape(contact), Kopete::Message::unescape(m_Nickname), message); + else + emit incomingPrivAction(Kopete::Message::unescape(m_Nickname), Kopete::Message::unescape(contact), message); + } +} + +void Engine::CtcpQuery_action(Message &msg) +{ + QString target = msg.arg(0); + if (target[0] == '#' || target[0] == '!' || target[0] == '&') + emit incomingAction(target, msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw()); + else + emit incomingPrivAction(msg.nickFromPrefix(), Kopete::Message::unescape(target), msg.ctcpMessage().ctcpRaw()); +} + +/* +NO REPLY EXIST FOR THE CTCP ACTION COMMAND ! +bool Engine::CtcpReply_action(Message &msg) +{ +} +*/ + +// FIXME: the API can now answer to help commands. +void Engine::CtcpQuery_clientinfo(Message &msg) +{ + QString clientinfo = customCtcpMap[ QString::fromLatin1("clientinfo") ]; + + if (clientinfo.isNull()) + clientinfo = QString::fromLatin1("The following commands are supported, but " + "without sub-command help: VERSION, CLIENTINFO, USERINFO, TIME, SOURCE, PING," + "ACTION."); + + writeCtcpReplyMessage( msg.nickFromPrefix(), QString::null, + msg.ctcpMessage().command(), QString::null, clientinfo); +} + +void Engine::CtcpRequest_dcc(const QString &nickname, const QString &fileName, uint port, Transfer::Type type) +{ + if( m_status != Connected || + m_sock->localAddress() == 0 || + m_sock->localAddress()->nodeName().isNull()) + return; + + switch(type) + { + case Transfer::Chat: + { + writeCtcpQueryMessage(nickname, QString::null, + QString::fromLatin1("DCC"), + QStringList(QString::fromLatin1("CHAT")) << QString::fromLatin1("chat") << + m_sock->localAddress()->nodeName() << QString::number(port) + ); + break; + } + + case Transfer::FileOutgoing: + { + QFileInfo file(fileName); + QString noWhiteSpace = file.fileName(); + if (noWhiteSpace.contains(' ') > 0) + noWhiteSpace.replace(QRegExp("\\s+"), "_"); + + TransferServer *server = TransferHandler::self()->createServer(this, nickname, type, fileName, file.size()); + + QString ip = m_sock->localAddress()->nodeName(); + QString ipNumber = QString::number( ntohl( inet_addr( ip.latin1() ) ) ); + + kdDebug(14120) << "Starting DCC file outgoing transfer." << endl; + + writeCtcpQueryMessage(nickname, QString::null, + QString::fromLatin1("DCC"), + QStringList(QString::fromLatin1("SEND")) << noWhiteSpace << ipNumber << + QString::number(server->port()) << QString::number(file.size()) + ); + break; + } + + case Transfer::FileIncoming: + case Transfer::Unknown: + default: + break; + } +} + +void Engine::CtcpQuery_dcc(Message &msg) +{ + Message &ctcpMsg = msg.ctcpMessage(); + QString dccCommand = ctcpMsg.arg(0).upper(); + + if (dccCommand == QString::fromLatin1("CHAT")) + { +// if(ctcpMsg.argsSize()!=4) return false; + + /* DCC CHAT type longip port + * + * type = Either Chat or Talk, but almost always Chat these days + * longip = 32-bit Internet address of originator's machine + * port = Port on which the originator is waitng for a DCC chat + */ + bool okayHost, okayPort; + // should ctctMsg.arg(1) be tested? + QHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost)); + unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort); + if (okayHost && okayPort) + { + kdDebug(14120) << "Starting DCC chat window." << endl; + TransferHandler::self()->createClient( + this, msg.nickFromPrefix(), + address, port, + Transfer::Chat ); + } + } + else if (dccCommand == QString::fromLatin1("SEND")) + { +// if(ctcpMsg.argsSize()!=5) return false; + + /* DCC SEND (filename) (longip) (port) (filesize) + * + * filename = Name of file being sent + * longip = 32-bit Internet address of originator's machine + * port = Port on which the originator is waiitng for a DCC chat + * filesize = Size of file being sent + */ + bool okayHost, okayPort, okaySize; +// QFileInfo realfile(msg.arg(1)); + QHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost)); + unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort); + unsigned int size = ctcpMsg.arg(4).toUInt(&okaySize); + if (okayHost && okayPort && okaySize) + { + kdDebug(14120) << "Starting DCC send file transfert for file:" << ctcpMsg.arg(1) << endl; + TransferHandler::self()->createClient( + this, msg.nickFromPrefix(), + address, port, + Transfer::FileIncoming, + ctcpMsg.arg(1), size ); + } + } +// else +// ((MessageRedirector *)sender())->error("Unknow dcc command"); +} + +/* +NO REPLY EXIST FOR THE CTCP DCC COMMAND ! +bool Engine::CtcpReply_dcc(Message &msg) +{ +} +*/ + +void Engine::CtcpReply_errmsg(Message &) +{ + // should emit one signal +} + +void Engine::CtcpQuery_finger( Message &) +{ + // To be implemented +} + +void Engine::CtcpRequest_ping(const QString &target) +{ + kdDebug(14120) << k_funcinfo << endl; + + timeval time; + if (gettimeofday(&time, 0) == 0) + { + QString timeReply; + + if( Entity::isChannel(target) ) + timeReply = QString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec); + else + timeReply = QString::number( time.tv_sec ); + + writeCtcpQueryMessage( target, QString::null, "PING", timeReply); + } +// else +// ((MessageRedirector *)sender())->error("failed to get current time"); +} + +void Engine::CtcpQuery_ping(Message &msg) +{ + writeCtcpReplyMessage( msg.nickFromPrefix(), QString::null, + msg.ctcpMessage().command(), msg.ctcpMessage().arg(0)); +} + +void Engine::CtcpReply_ping(Message &msg) +{ + timeval time; + if (gettimeofday(&time, 0) == 0) + { + // FIXME: the time code is wrong for usec + QString timeReply = QString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec); + double newTime = timeReply.toDouble(); + double oldTime = msg.suffix().section(' ',0, 0).toDouble(); + double difference = newTime - oldTime; + QString diffString; + + if (difference < 1) + { + diffString = QString::number(difference); + diffString.remove((diffString.find('.') -1), 2); + diffString.truncate(3); + diffString.append("milliseconds"); + } + else + { + diffString = QString::number(difference); + QString seconds = diffString.section('.', 0, 0); + QString millSec = diffString.section('.', 1, 1); + millSec.remove(millSec.find('.'), 1); + millSec.truncate(3); + diffString = QString::fromLatin1("%1 seconds, %2 milliseconds").arg(seconds).arg(millSec); + } + + emit incomingCtcpReply(QString::fromLatin1("PING"), msg.nickFromPrefix(), diffString); + } +// else +// ((MessageRedirector *)sender())->error("failed to get current time"); +} + +void Engine::CtcpQuery_source(Message &msg) +{ + writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null, + msg.ctcpMessage().command(), m_SourceString); +} + +void Engine::CtcpQuery_time(Message &msg) +{ + writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null, + msg.ctcpMessage().command(), QDateTime::currentDateTime().toString(), + QString::null, false); +} + +void Engine::CtcpQuery_userinfo(Message &msg) +{ + QString userinfo = customCtcpMap[ QString::fromLatin1("userinfo") ]; + + if (userinfo.isNull()) + userinfo = m_UserString; + + writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null, + msg.ctcpMessage().command(), QString::null, userinfo); +} + +void Engine::CtcpRequest_version(const QString &target) +{ + writeCtcpQueryMessage(target, QString::null, "VERSION"); +} + +void Engine::CtcpQuery_version(Message &msg) +{ + QString response = customCtcpMap[ QString::fromLatin1("version") ]; + kdDebug(14120) << "Version check: " << response << endl; + + if (response.isNull()) + response = m_VersionString; + + writeCtcpReplyMessage(msg.nickFromPrefix(), + msg.ctcpMessage().command() + " " + response); +} + +void Engine::CtcpReply_version(Message &msg) +{ + emit incomingCtcpReply(msg.ctcpMessage().command(), msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw()); +} diff --git a/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp b/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp new file mode 100644 index 00000000..c47b8b05 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp @@ -0,0 +1,570 @@ + +/* + kircnumericreplies.cpp - IRC Client + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2003 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 "kircengine.h" + +#include <qtimer.h> + +using namespace KIRC; + +/* IMPORTANT NOTE: + * Numeric replies always have the current nick or * as first argmuent. + * NOTE: * means undefined in most (all ?) of the cases. + */ + +void Engine::bindNumericReplies() +{ + bind(1, this, SLOT(numericReply_001(KIRC::Message &)), 1, 1); + bind(2, this, SLOT(numericReply_002(KIRC::Message &)), 1, 1); + bind(3, this, SLOT(numericReply_003(KIRC::Message &)), 1, 1); + bind(4, this, SLOT(numericReply_004(KIRC::Message &)), 5, 5); + bind(5, this, SLOT(numericReply_004(KIRC::Message &)), 1, 1); + + bind(250, this, SLOT(numericReply_250(KIRC::Message &))); + bind(251, this, SLOT(numericReply_251(KIRC::Message &))); + bind(252, this, SLOT(numericReply_252(KIRC::Message &)), 2, 2); + bind(253, this, SLOT(numericReply_253(KIRC::Message &)), 2, 2); + bind(254, this, SLOT(numericReply_254(KIRC::Message &)), 2, 2); + bind(255, this, SLOT(numericReply_255(KIRC::Message &)), 1, 1); // incomingConnectString + + bind(263, this, SLOT(numericReply_263(KIRC::Message &))); // incomingServerLoadTooHigh + bind(265, this, SLOT(numericReply_265(KIRC::Message &))); + bind(266, this, SLOT(numericReply_266(KIRC::Message &))); + + bind(301, this, SLOT(numericReply_301(KIRC::Message &)), 2, 2); + bind(303, this, SLOT(numericReply_303(KIRC::Message &)), 1, 1); + bind(305, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 ); // You are no longer marked as away + bind(306, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 ); // You are marked as away + bind(307, this, SLOT(numericReply_307(KIRC::Message &)), 1, 1); + bind(311, this, SLOT(numericReply_311(KIRC::Message &)), 5, 5); + bind(312, this, SLOT(numericReply_312(KIRC::Message &)), 3, 3); + bind(313, this, SLOT(numericReply_313(KIRC::Message &)), 2, 2); + bind(314, this, SLOT(numericReply_314(KIRC::Message &)), 5, 5); + bind(315, this, SLOT(numericReply_315(KIRC::Message &)), 2, 2); + bind(317, this, SLOT(numericReply_317(KIRC::Message &)), 3, 4); + bind(318, this, SLOT(numericReply_318(KIRC::Message &)), 2, 2); + bind(319, this, SLOT(numericReply_319(KIRC::Message &)), 2, 2); + bind(320, this, SLOT(numericReply_320(KIRC::Message &)), 2, 2); + bind(321, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 ); + bind(322, this, SLOT(numericReply_322(KIRC::Message &)), 3, 3); + bind(323, this, SLOT(numericReply_323(KIRC::Message &)), 1, 1); + bind(324, this, SLOT(numericReply_324(KIRC::Message &)), 2, 4); + bind(328, this, SLOT(numericReply_328(KIRC::Message &)), 2, 2); + bind(329, this, SLOT(numericReply_329(KIRC::Message &)), 3, 3); + bind(330, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0); // ??? + bind(331, this, SLOT(numericReply_331(KIRC::Message &)), 2, 2); + bind(332, this, SLOT(numericReply_332(KIRC::Message &)), 2, 2); + bind(333, this, SLOT(numericReply_333(KIRC::Message &)), 4, 4); + bind(352, this, SLOT(numericReply_352(KIRC::Message &)), 5, 10); + bind(353, this, SLOT(numericReply_353(KIRC::Message &)), 3, 3); + bind(366, this, SLOT(numericReply_366(KIRC::Message &)), 2, 2); + bind(369, this, SLOT(numericReply_369(KIRC::Message &)), 2, 2); + bind(372, this, SLOT(numericReply_372(KIRC::Message &)), 1, 1); + bind(375, this, SLOT(ignoreMessage(KIRC::Message&)), 0, 0 ); + bind(376, this, SLOT(ignoreMessage(KIRC::Message&)), 0, 0 ); + + bind(401, this, SLOT(numericReply_401(KIRC::Message &)), 2, 2); // incomingNoNickChan +// bind(404, this, SLOT(numericReply_404(KIRC::Message &)), 2, 2); // incomingCannotSendToChannel + bind(406, this, SLOT(numericReply_406(KIRC::Message &)), 2, 2); // incomingWasNoNick + bind(422, this, SLOT(numericReply_422(KIRC::Message &)), 1, 1); + bind(433, this, SLOT(numericReply_433(KIRC::Message &)), 2, 2); +// bind(442, this, SLOT(numericReply_442(KIRC::Message &)), 2, 2); // incomingCannotSendToChannel + bind(464, this, SLOT(numericReply_464(KIRC::Message &)), 1, 1); + bind(471, this, SLOT(numericReply_471(KIRC::Message &)), 2, 2); + bind(473, this, SLOT(numericReply_473(KIRC::Message &)), 2, 2); + bind(474, this, SLOT(numericReply_474(KIRC::Message &)), 2, 2); + bind(475, this, SLOT(numericReply_475(KIRC::Message &)), 2, 2); + + //Freenode seems to use this for a non-RFC compliant purpose, as does Unreal + bind(477, this, SLOT(emitSuffix(KIRC::Message&)),0,0); +} + +/* 001: "Welcome to the Internet Relay Network <nick>!<user>@<host>" + * Gives a welcome message in the form of: + */ +void Engine::numericReply_001(Message &msg) +{ + kdDebug(14121) << k_funcinfo << endl; + + if (m_FailedNickOnLogin) + { + // this is if we had a "Nickname in use" message when connecting and we set another nick. + // This signal emits that the nick was accepted and we are now logged in + emit successfullyChangedNick(m_Nickname, m_PendingNick); + m_Nickname = m_PendingNick; + m_FailedNickOnLogin = false; + } + + /* At this point we are connected and the server is ready for us to being taking commands + * although the MOTD comes *after* this. + */ + emitSuffix(msg); + + setStatus(Connected); +} + +/* 002: ":Your host is <servername>, running version <ver>" + * Gives information about the host. The given informations are close to 004. + */ +void Engine::numericReply_002(Message &msg) +{ + emitSuffix(msg); +} + +/* 003: "This server was created <date>" + * Gives the date that this server was created. + * NOTE: This is useful for determining the uptime of the server). + */ +void Engine::numericReply_003(Message &msg) +{ + emitSuffix(msg); +} + +/* 004: "<servername> <version> <available user modes> <available channel modes>" + * Gives information about the servername, version, available modes, etc. + */ +void Engine::numericReply_004(Message &msg) +{ + emit incomingHostInfo(msg.arg(1),msg.arg(2),msg.arg(3),msg.arg(4)); +} + +/* 005: + * Gives capability information. TODO: This is important! + */ +void Engine::numericReply_005(Message &msg) +{ + emit incomingConnectString( msg.toString() ); +} + +/* 250: ":Highest connection count: <integer> (<integer> clients) + * (<integer> since server was (re)started)" + * Tells connections statistics about the server for the uptime activity. + * NOT IN RFC1459 NOR RFC2812 + */ +void Engine::numericReply_250(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} + +/* 251: ":There are <integer> users and <integer> services on <integer> servers" + * Tells how many user there are on all the different servers in the form of: + */ +void Engine::numericReply_251(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} +/* 252: "<integer> :operator(s) online" + * Issues a number of operators on the server in the form of: + */ +void Engine::numericReply_252(Message &msg) +{ + emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() ); +} + +/* 253: "<integer> :unknown connection(s)" + * Tells how many unknown connections the server has in the form of: + */ +void Engine::numericReply_253(Message &msg) +{ + emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() ); +} + +/* Tells how many total channels there are on this network in the form of: + * "<integer> :channels formed" */ +void Engine::numericReply_254(Message &msg) +{ + emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() ); +} + +/* 255: ":I have <integer> clients and <integer> servers" + * Tells how many clients and servers *this* server handles. + */ +void Engine::numericReply_255(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} + +/* 263: + * Server is too busy. + */ +void Engine::numericReply_263(Message &) +{ + emit incomingServerLoadTooHigh(); +} + +/* 265: ":Current local users: <integer> Max: <integer>" + * Tells statistics about the current local server state. + * NOT IN RFC2812 + */ +void Engine::numericReply_265(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} + +/* 266: ":Current global users: <integer> Max: <integer>" + * Tells statistics about the current global(the whole irc server chain) server state: + */ +void Engine::numericReply_266(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} + +/* 301: "<nick> :<away message>" + */ +void Engine::numericReply_301(Message &msg) +{ + emit incomingUserIsAway(Kopete::Message::unescape(msg.arg(1)), msg.suffix()); +} + +/* 303: ":*1<nick> *(" " <nick> )" + */ +void Engine::numericReply_303(Message &msg) +{ + QStringList nicks = QStringList::split(QRegExp(QChar(' ')), msg.suffix()); + for(QStringList::Iterator it = nicks.begin(); it != nicks.end(); ++it) + { + if (!(*it).stripWhiteSpace().isEmpty()) + emit incomingUserOnline(Kopete::Message::unescape(*it)); + } +} + +/* 305: ":You are no longer marked as being away" + */ +// void Engine::numericReply_305(Message &msg) +// { +// } + + +/* 306: ":You have been marked as being away" + */ +// void Engine::numericReply_306(Message &msg) +// { +// } + +/* 307: ":is a registered nick" + * DALNET: Indicates that this user is identified with NICSERV. + */ +void Engine::numericReply_307(Message & /*msg*/) +{ +// emit incomingWhoiIsUserNickIsRegistered(Kopete::Message::unescape(msg.arg(1))); +} + +/* 311: "<nick> <user> <host> * :<real name>" + * Show info about a user (part of a /whois) in the form of: + */ +void Engine::numericReply_311(Message &msg) +{ + emit incomingWhoIsUser(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3), msg.suffix()); +} + +/* 312: "<nick> <server> :<server info>" + * Show info about a server (part of a /whois). + */ +void Engine::numericReply_312(Message &msg) +{ + emit incomingWhoIsServer(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.suffix()); +} + +/* 313: "<nick> :is an IRC operator" + * Show info about an operator (part of a /whois). + */ +void Engine::numericReply_313(Message & /*msg*/) +{ +} + +/* 314: "<nick> <user> <host> * :<real name>" + * Show WHOWAS Info + */ +void Engine::numericReply_314(Message &msg) +{ + emit incomingWhoWasUser(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3), msg.suffix()); +} + +void Engine::numericReply_315(Message &msg) +{ + emit incomingEndOfWho(Kopete::Message::unescape(msg.arg(1))); +} + +void Engine::numericReply_317(Message &msg) +{ + /* RFC say: "<nick> <integer> :seconds idle" + * Some servers say: "<nick> <integer> <integer> :seconds idle, signon time" + * Show info about someone who is idle (part of a /whois) in the form of: + */ + emit incomingWhoIsIdle(Kopete::Message::unescape(msg.arg(1)), msg.arg(2).toULong()); + if (msg.argsSize()==4) + emit incomingSignOnTime(Kopete::Message::unescape(msg.arg(1)),msg.arg(3).toULong()); +} + +/* 318: "<nick>{<space><realname>} :End of /WHOIS list" + * End of WHOIS for a given nick. + */ +void Engine::numericReply_318(Message &msg) +{ + emit incomingEndOfWhois(Kopete::Message::unescape(msg.arg(1))); +} + +void Engine::numericReply_319(Message &msg) +{ + /* Show info a channel a user is logged in (part of a /whois) in the form of: + * "<nick> :{[@|+]<channel><space>}" + */ + emit incomingWhoIsChannels(Kopete::Message::unescape(msg.arg(1)), msg.suffix()); +} + +/* 320: + * Indicates that this user is identified with NICSERV on FREENODE. + */ +void Engine::numericReply_320(Message &msg) +{ + emit incomingWhoIsIdentified(Kopete::Message::unescape(msg.arg(1))); +} + +/* 321: "<channel> :Users Name" ("Channel :Users Name") + * RFC1459: Declared. + * RFC2812: Obsoleted. + */ + +/* 322: "<channel> <# visible> :<topic>" + * Received one channel from the LIST command. + */ +void Engine::numericReply_322(Message &msg) +{ + //kdDebug(14120) << k_funcinfo << "Listed " << msg.arg(1) << endl; + + emit incomingListedChan(Kopete::Message::unescape(msg.arg(1)), msg.arg(2).toUInt(), msg.suffix()); +} + +/* 323: ":End of LIST" + * End of the LIST command. + */ +void Engine::numericReply_323(Message &) +{ + emit incomingEndOfList(); +} + +/* 324: "<channel> <mode> <mode params>" + */ +void Engine::numericReply_324(Message &msg) +{ + emit incomingChannelMode(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3)); +} + +/* 328: "<channel> <mode> <mode params>" + */ +void Engine::numericReply_328(Message &msg) +{ + kdDebug(14120) << k_funcinfo << endl; + emit incomingChannelHomePage(Kopete::Message::unescape(msg.arg(1)), msg.suffix()); +} + +/* 329: "%s %lu" + * NOTE: What is the meaning of this arguments. DAL-ircd say it's a RPL_CREATIONTIME + * NOT IN RFC1459 NOR RFC2812 + */ +void Engine::numericReply_329( Message &) +{ +} + +/* 331: "<channel> :No topic is set" + * Gives the existing topic for a channel after a join. + */ +void Engine::numericReply_331( Message &) +{ +// emit incomingExistingTopic(msg.arg(1), suffix); +} + +/* 332: "<channel> :<topic>" + * Gives the existing topic for a channel after a join. + */ +void Engine::numericReply_332(Message &msg) +{ + emit incomingExistingTopic(Kopete::Message::unescape(msg.arg(1)), msg.suffix()); +} + +/* 333: + * Gives the nickname and time who changed the topic + */ +void Engine::numericReply_333( Message &msg ) +{ + kdDebug(14120) << k_funcinfo << endl; + QDateTime d; + d.setTime_t( msg.arg(3).toLong() ); + emit incomingTopicUser( Kopete::Message::unescape(msg.arg(1)), Kopete::Message::unescape(msg.arg(2)), d ); +} + +/* 352: + * WHO Reply + * + * "<channel> <user> <host> <server> <nick> ("H" / "G") ["*"] [("@" / "+")] :<hopcount> <real name>" + * + * :efnet.cs.hut.fi 352 userNick #foobar username some.host.name efnet.cs.hut.fi someNick H :0 foobar + * :efnet.cs.hut.fi 352 userNick #foobar ~fooobar other.hostname irc.dkom.at anotherNick G+ :3 Unknown + */ +void Engine::numericReply_352(Message &msg) +{ + emit incomingWhoReply( + Kopete::Message::unescape(msg.arg(5)), // nick name + Kopete::Message::unescape(msg.arg(1)), // channel name + msg.arg(2), // user name + msg.arg(3), // host name + msg.arg(4), // server name + msg.arg(6)[0] != 'H', // G=away (true), H=not away (false) + msg.arg(7), // @ (op), + (voiced) + msg.suffix().section(' ', 0, 1 ).toUInt(), // hopcount + msg.suffix().section(' ', 1 ) // real name + ); +} + + +/* 353: + * NAMES list + */ +void Engine::numericReply_353(Message &msg) +{ + emit incomingNamesList(Kopete::Message::unescape(msg.arg(2)), QStringList::split(' ', msg.suffix())); +} + +/* 366: "<channel> :End of NAMES list" + * Gives a signal to indicate that the NAMES list has ended for channel. + */ +void Engine::numericReply_366(Message &msg) +{ + emit incomingEndOfNames(msg.arg(1)); +} + +/* 369: + * End of WHOWAS Request + */ +void Engine::numericReply_369(Message & /*msg*/) +{ +} + +/* 372: ":- <text>" + * Part of the MOTD. + */ +void Engine::numericReply_372(Message &msg) +{ + emit incomingMotd(msg.suffix()); +} + +/* 375: ":- <server> Message of the day - " + * Beginging the motd. This isn't emitted because the MOTD is sent out line by line. + */ + +/* 376: ":End of MOTD command" + * End of the motd. + */ + +/* 401: "<nickname> :No such nick/channel" + * Gives a signal to indicate that the command issued failed because the person/channel not being on IRC. + * - Used to indicate the nickname parameter supplied to a command is currently unused. + */ +void Engine::numericReply_401(Message &msg) +{ + emit incomingNoSuchNickname( Kopete::Message::unescape(msg.arg(1)) ); +} + +/* 406: "<nickname> :There was no such nickname" + * Like case 401, but when there *was* no such nickname. + */ +void Engine::numericReply_406(Message &msg) +{ + emit incomingNoSuchNickname( Kopete::Message::unescape(msg.arg(1)) ); +} + +/* 422: ":MOTD File is missing" + * + * Server's MOTD file could not be opened by the server. + */ +void Engine::numericReply_422(Message &msg) +{ + emit incomingMotd(msg.suffix()); +} + +/* 433: "<nick> :Nickname is already in use" + * Tells us that our nickname is already in use. + */ +void Engine::numericReply_433(Message &msg) +{ + if(m_status == Authentifying) + { + // This tells us that our nickname is, but we aren't logged in. + // This differs because the server won't send us a response back telling us our nick changed + // (since we aren't logged in). + m_FailedNickOnLogin = true; + emit incomingFailedNickOnLogin(Kopete::Message::unescape(msg.arg(1))); + } + else + { + // And this is the signal for if someone is trying to use the /nick command or such when already logged in, + // but it's already in use + emit incomingNickInUse(Kopete::Message::unescape(msg.arg(1))); + } +} + +/* 464: ":Password Incorrect" + * Bad server password + */ +void Engine::numericReply_464(Message &/*msg*/) +{ + /* Server need pass.. Call disconnect*/ + emit incomingFailedServerPassword(); +} + +/* 471: + * Channel is Full + */ +void Engine::numericReply_471(Message &msg) +{ + emit incomingFailedChanFull(Kopete::Message::unescape(msg.arg(1))); +} + +/* 473: + * Invite Only. + */ +void Engine::numericReply_473(Message &msg) +{ + emit incomingFailedChanInvite(Kopete::Message::unescape(msg.arg(1))); +} + +/* 474: + * Banned. + */ +void Engine::numericReply_474(Message &msg) +{ + emit incomingFailedChanBanned(Kopete::Message::unescape(msg.arg(1))); +} + +/* 475: + * Wrong Chan-key. + */ +void Engine::numericReply_475(Message &msg) +{ + emit incomingFailedChankey(Kopete::Message::unescape(msg.arg(1))); +} + +/* 477: "<channel> :You need a registered nick to join that channel." + * Available on DALNET servers only ? + */ +// void Engine::numericReply_477(Message &msg) +// { +// emit incomingChannelNeedRegistration(msg.arg(2), msg.suffix()); +// } diff --git a/kopete/protocols/irc/libkirc/kircentity.cpp b/kopete/protocols/irc/libkirc/kircentity.cpp new file mode 100644 index 00000000..6aa6fd55 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircentity.cpp @@ -0,0 +1,132 @@ +/* + kircentity.cpp - IRC Client + + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 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 "kircengine.h" +#include "kircentity.h" + +#include <kdebug.h> + +using namespace KIRC; +using namespace KNetwork; + +/** + * Match a possible user definition: + * nick!user@host + * where user and host are optionnal. + * NOTE: If changes are done to the regexp string, update also the sm_userStrictRegExp regexp string. + */ +const QRegExp Entity::sm_userRegExp(QString::fromLatin1("^([^\\s,:!@]+)(?:(?:!([^\\s,:!@]+))?(?:@([^\\s,!@]+)))?$")); + +/** + * Regexp to match strictly the complete user definition: + * nick!user@host + * NOTE: If changes are done to the regexp string, update also the sm_userRegExp regexp string. + */ +const QRegExp Entity::sm_userStrictRegExp(QString::fromLatin1("^([^\\s,:!@]+)!([^\\s,:!@]+)@([^\\s,:!@]+)$")); + +const QRegExp Entity::sm_channelRegExp( QString::fromLatin1("^[#!+&][^\\s,]+$") ); + +Entity::Entity(const QString &, const Type type) + : QObject(0, "KIRC::Entity"), + m_type(type) +{ +// rename(name, type); +} + +Entity::~Entity() +{ + emit destroyed(this); +} + +QString Entity::name() const +{ + return m_name; +} + +QString Entity::host() const +{ + switch(m_type) + { +// case Unknown: + case Server: + return m_name; +// case Channel: + case Service: + case User: + return userHost(); + default: + kdDebug(14121) << k_funcinfo << "No host defined for type:" << m_type; + return QString::null; + } +} + +KIRC::Entity::Type Entity::type() const +{ + return m_type; +} + +KIRC::Entity::Type Entity::guessType() +{ + m_type = guessType(m_name); + return m_type; +} + +// FIXME: Implement me +KIRC::Entity::Type Entity::guessType(const QString &) +{ + return Unknown; +} + +QString Entity::userNick() const +{ + return userNick(m_name); +} + +QString Entity::userNick(const QString &s) +{ + return userInfo(s, 1); +} + +QString Entity::userName() const +{ + return userName(m_name); +} + +QString Entity::userName(const QString &s) +{ + return userInfo(s, 2); +} + +QString Entity::userHost() const +{ + return userHost(m_name); +} + +QString Entity::userHost(const QString &s) +{ + return userInfo(s, 3); +} + +QString Entity::userInfo(const QString &s, int num) +{ + QRegExp userRegExp(sm_userRegExp); + userRegExp.search(s); + return userRegExp.cap(num); +} + +#include "kircentity.moc" + diff --git a/kopete/protocols/irc/libkirc/kircentity.h b/kopete/protocols/irc/libkirc/kircentity.h new file mode 100644 index 00000000..c9336439 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircentity.h @@ -0,0 +1,128 @@ +/* + kircentity.h - IRC Client + + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCENTITY_H +#define KIRCENTITY_H + +#include <kdeversion.h> +#include <kresolver.h> +#include <ksharedptr.h> + +#include <qobject.h> +#include <qregexp.h> +#include <qstring.h> +#include <qvaluelist.h> + +namespace KIRC +{ + +class Engine; + +class Entity + : public QObject, + public KShared +{ + Q_OBJECT + +public: + typedef enum Type + { + Unknown, + Server, + Channel, + Service, + User + }; + + Entity(const QString &name, const Type type = Unknown); + virtual ~Entity(); + + QString name() const; + QString host() const; + + KIRC::Entity::Type type() const; + KIRC::Entity::Type guessType(); + static KIRC::Entity::Type guessType(const QString &name); + + // FIXME: Remove these is* functions ... They are duplicate with the ::guessType(const QString&) + inline static bool isUser( const QString &s ) + { return sm_userRegExp.exactMatch(s); }; + inline bool isChannel() + { return isChannel(m_name); }; + inline static bool isChannel( const QString &s ) + { return sm_channelRegExp.exactMatch(s); }; + + QString userNick() const; + static QString userNick(const QString &s); + + QString userName() const; + static QString userName(const QString &s); + + QString userHost() const; + static QString userHost(const QString &s); + +signals: + void destroyed(KIRC::Entity *self); + +private: + + static QString userInfo(const QString &s, int num_cap); + + static const QRegExp sm_userRegExp; + static const QRegExp sm_userStrictRegExp; + static const QRegExp sm_channelRegExp; + + KIRC::Entity::Type m_type; + QString m_name; + + // peer ip address if the entity is a User. + QString m_address; +}; + +class EntityPtr + : public KSharedPtr<KIRC::Entity> +{ +public: + EntityPtr(KIRC::Entity *entity = 0) + : KSharedPtr<KIRC::Entity>(entity) + { } + + EntityPtr(const KIRC::EntityPtr &entity) + : KSharedPtr<KIRC::Entity>(entity) + { } +}; + +class EntityPtrList + : public QValueList<EntityPtr> +{ +public: + EntityPtrList() + { } + + EntityPtrList(const EntityPtr &entity) + { + append(entity); + } + + EntityPtrList(const QValueList<EntityPtr> &list) + : QValueList<EntityPtr>(list) + { } +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/kircmessage.cpp b/kopete/protocols/irc/libkirc/kircmessage.cpp new file mode 100644 index 00000000..f1a5b61f --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessage.cpp @@ -0,0 +1,370 @@ +/* + kircmessage.cpp - IRC Client + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 by the Kopete engineelopers <kopete-engineel@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 "kircengine.h" +#include "kircmessage.h" + +// FIXME: Remove the following dependencies. +#include "kopetemessage.h" +#include "ksparser.h" + +#include <kdebug.h> +#include <kextsock.h> +#include <klocale.h> +#include <kmessagebox.h> + +using namespace KIRC; + +#ifndef _IRC_STRICTNESS_ +QRegExp Message::m_IRCNumericCommand("^\\d{1,3}$"); + +// TODO: This regexp parsing is no good. It's slower than it needs to be, and +// is not codec-safe since QString requires a codec. NEed to parse this with +// our own parsing class that operates on the raw QCStrings +QRegExp Message::m_IRCCommandType1( + "^(?::([^ ]+) )?([A-Za-z]+|\\d{1,3})((?: [^ :][^ ]*)*) ?(?: :(.*))?$"); + // Extra end arg space check -------------------------^ +#else // _IRC_STRICTNESS_ +QRegExp Message::m_IRCNumericCommand("^\\d{3,3}$"); + +QRegExp Message::m_IRCCommandType1( + "^(?::([^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){0,13})(?: :(.*))?$"); +QRegExp Message::m_IRCCommandType2( + "^(?::[[^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){14,14})(?: (.*))?$"); +#endif // _IRC_STRICTNESS_ + +Message::Message() + : m_ctcpMessage(0) +{ +} + +Message::Message(const Message &obj) + : m_ctcpMessage(0) +{ + m_raw = obj.m_raw; + + m_prefix = obj.m_prefix; + m_command = obj.m_command; + m_args = obj.m_args; + m_suffix = obj.m_suffix; + + m_ctcpRaw = obj.m_ctcpRaw; + + if (obj.m_ctcpMessage) + m_ctcpMessage = new Message(obj.m_ctcpMessage); +} + +Message::Message(const Message *obj) + : m_ctcpMessage(0) +{ + m_raw = obj->m_raw; + + m_prefix = obj->m_prefix; + m_command = obj->m_command; + m_args = obj->m_args; + m_suffix = obj->m_suffix; + + m_ctcpRaw = obj->m_ctcpRaw; + + if (obj->m_ctcpMessage) + m_ctcpMessage = new Message(obj->m_ctcpMessage); +} + +Message::~Message() +{ + if (m_ctcpMessage) + delete m_ctcpMessage; +} + +void Message::writeRawMessage(Engine *engine, const QTextCodec *codec, const QString &str) +{ + // FIXME: Really handle this + if (!engine->socket()) + { + kdDebug(14121) << k_funcinfo << "Not connected while attempting to write:" << str << endl; + return; + } + + QString txt = str + QString::fromLatin1("\r\n"); + + QCString s(codec->fromUnicode(txt)); + kdDebug(14120) << "Message is " << s.length() << " chars" << endl; + // FIXME: Should check the amount of data really writen. + int wrote = engine->socket()->writeBlock(s.data(), s.length()); + + kdDebug(14121) << QString::fromLatin1("(%1 bytes) >> %2").arg(wrote).arg(str) << endl; +} + +void Message::writeMessage(Engine *engine, const QTextCodec *codec, const QString &message) +{ + writeRawMessage(engine, codec, quote(message)); +} + +void Message::writeMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QStringList &args, const QString &suffix) +{ + QString msg = command; + + if (!args.isEmpty()) + msg += QChar(' ') + args.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here + + if (!suffix.isNull()) + msg = msg.stripWhiteSpace() + QString::fromLatin1(" :") + suffix; + + writeMessage(engine, codec, msg); +} + +void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QString&to, + const QString &ctcpMessage) +{ + writeMessage(engine, codec, command, to, QChar(0x01) + ctcpQuote(ctcpMessage) + QChar(0x01)); +} + +void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix ) +{ + QString ctcpMsg = ctcpCommand; + + if (!ctcpArgs.isEmpty()) + ctcpMsg += QChar(' ') + ctcpArgs.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here + + if (!ctcpSuffix.isNull()) + ctcpMsg += QString::fromLatin1(" :") + ctcpSuffix; + + writeMessage(engine, codec, command, to, suffix + QChar(0x01) + ctcpQuote(ctcpMsg) + QChar(0x01)); +} + +Message Message::parse(Engine *engine, const QTextCodec *codec, bool *parseSuccess) +{ + if (parseSuccess) + *parseSuccess=false; + + if (engine->socket()->canReadLine()) + { + QCString raw(engine->socket()->bytesAvailable()+1); + Q_LONG length = engine->socket()->readLine(raw.data(), raw.count()); + + if( length > -1 ) + { + raw.resize( length ); + + // Remove trailing '\r\n' or '\n'. + // + // Some servers send '\n' instead of '\r\n' that the RFCs say they should be sending. + + if (length > 1 && raw.at(length-2) == '\n') { + raw.at(length-2) = '\0'; + } + if (length > 2 && raw.at(length-3) == '\r') { + raw.at(length-3) = '\0'; + } + + kdDebug(14121) << "<< " << raw << endl; + + Message msg; + if(matchForIRCRegExp(raw, codec, msg)) + { + if(parseSuccess) + *parseSuccess = true; + } + else + { + kdDebug(14120) << k_funcinfo << "Unmatched line: \"" << raw << "\"" << endl; + } + + return msg; + } + else + kdWarning(14121) << k_funcinfo << "Failed to read a line while canReadLine returned true!" << endl; + } + + return Message(); +} + +QString Message::quote(const QString &str) +{ + QString tmp = str; + QChar q('\020'); + tmp.replace(q, q+QString(q)); + tmp.replace(QChar('\r'), q+QString::fromLatin1("r")); + tmp.replace(QChar('\n'), q+QString::fromLatin1("n")); + tmp.replace(QChar('\0'), q+QString::fromLatin1("0")); + return tmp; +} + +// FIXME: The unquote system is buggy. +QString Message::unquote(const QString &str) +{ + QString tmp = str; + + char b[3] = { 020, 020, '\0' }; + const char b2[2] = { 020, '\0' }; + + tmp.replace( b, b2 ); + b[1] = 'r'; + tmp.replace( b, "\r"); + b[1] = 'n'; + tmp.replace( b, "\n"); + b[1] = '0'; + tmp.replace( b, "\0"); + + return tmp; +} + +QString Message::ctcpQuote(const QString &str) +{ + QString tmp = str; + tmp.replace( QChar('\\'), QString::fromLatin1("\\\\")); + tmp.replace( (char)1, QString::fromLatin1("\\1")); + return tmp; +} + +QString Message::ctcpUnquote(const QString &str) +{ + QString tmp = str; + tmp.replace("\\\\", "\\"); + tmp.replace("\\1", "\1" ); + return tmp; +} + +bool Message::matchForIRCRegExp(const QCString &line, const QTextCodec *codec, Message &message) +{ + if(matchForIRCRegExp(m_IRCCommandType1, codec, line, message)) + return true; +#ifdef _IRC_STRICTNESS_ + if(!matchForIRCRegExp(m_IRCCommandType2, codec, line, message)) + return true; +#endif // _IRC_STRICTNESS_ + return false; +} + +// FIXME: remove the decodeStrings calls or update them. +// FIXME: avoid the recursive call, it make the ctcp command unquoted twice (wich is wrong, but valid in most of the cases) +bool Message::matchForIRCRegExp(QRegExp ®exp, const QTextCodec *codec, const QCString &line, Message &msg ) +{ + if( regexp.exactMatch( codec->toUnicode(line) ) ) + { + msg.m_raw = line; + msg.m_prefix = unquote(regexp.cap(1)); + msg.m_command = unquote(regexp.cap(2)); + msg.m_args = QStringList::split(' ', regexp.cap(3)); + + QCString suffix = codec->fromUnicode(unquote(regexp.cap(4))); + if (!suffix.isNull() && suffix.length() > 0) + { + QCString ctcpRaw; + if (extractCtcpCommand(suffix, ctcpRaw)) + { + msg.m_ctcpRaw = codec->toUnicode(ctcpRaw); + + msg.m_ctcpMessage = new Message(); + msg.m_ctcpMessage->m_raw = codec->fromUnicode(ctcpUnquote(msg.m_ctcpRaw)); + + int space = ctcpRaw.find(' '); + if (!matchForIRCRegExp(msg.m_ctcpMessage->m_raw, codec, *msg.m_ctcpMessage)) + { + QCString command; + if (space > 0) + command = ctcpRaw.mid(0, space).upper(); + else + command = ctcpRaw.upper(); + msg.m_ctcpMessage->m_command = + Kopete::Message::decodeString( KSParser::parse(command), codec ); + } + + if (space > 0) + msg.m_ctcpMessage->m_ctcpRaw = + Kopete::Message::decodeString( KSParser::parse(ctcpRaw.mid(space)), codec ); + } + + msg.m_suffix = Kopete::Message::decodeString( KSParser::parse(suffix), codec ); + } + else + msg.m_suffix = QString::null; + return true; + } + return false; +} + +void Message::decodeAgain( const QTextCodec *codec ) +{ + matchForIRCRegExp(m_raw, codec, *this); +} + +// FIXME: there are missing parts +QString Message::toString() const +{ + if( !isValid() ) + return QString::null; + + QString msg = m_command; + for (QStringList::ConstIterator it = m_args.begin(); it != m_args.end(); ++it) + msg += QChar(' ') + *it; + if (!m_suffix.isNull()) + msg += QString::fromLatin1(" :") + m_suffix; + + return msg; +} + +bool Message::isNumeric() const +{ + return m_IRCNumericCommand.exactMatch(m_command); +} + +bool Message::isValid() const +{ +// This could/should be more complex but the message validity is tested durring the parsing +// So this is enougth as we don't allow the editing the content. + return !m_command.isEmpty(); +} + +/* Return true if the given string is a special command string + * (i.e start and finish with the ascii code \001), and the given + * string is splited to get the first part of the message and fill the ctcp command. + * FIXME: The code currently only match for a textual message or a ctcp message not both mixed as it can be (even if very rare). + */ +bool Message::extractCtcpCommand(QCString &message, QCString &ctcpline) +{ + uint len = message.length(); + + if( message[0] == 1 && message[len-1] == 1 ) + { + ctcpline = message.mid(1,len-2); + message.truncate(0); + + return true; + } + + return false; +} + +void Message::dump() const +{ + kdDebug(14120) << "Raw:" << m_raw << endl + << "Prefix:" << m_prefix << endl + << "Command:" << m_command << endl + << "Args:" << m_args << endl + << "Suffix:" << m_suffix << endl + << "CtcpRaw:" << m_ctcpRaw << endl; + if(m_ctcpMessage) + { + kdDebug(14120) << "Contains CTCP Message:" << endl; + m_ctcpMessage->dump(); + } +} diff --git a/kopete/protocols/irc/libkirc/kircmessage.h b/kopete/protocols/irc/libkirc/kircmessage.h new file mode 100644 index 00000000..e37f3fb2 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessage.h @@ -0,0 +1,198 @@ +/* + kircmessage.h - IRC Client + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCMESSAGE_H +#define KIRCMESSAGE_H + +#include "kircentity.h" + +#include <kbufferedio.h> + +#include <qdict.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtextcodec.h> +#include <qregexp.h> + +#include <kopetemessage.h> + +// Uncoment this if you want a really rfc compliant message handling. +// This is due to some changes of the message encoding with 14 arguments.(not very frequent :) +// #define _IRC_STRICTNESS_ + +namespace KIRC +{ + +class Engine; + +class Message +{ +public: + /** \brief Sends the message as-is to the server. + */ + static void writeRawMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str); + + static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str); + + static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QStringList &args, const QString &suffix); + + static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, + const QString &ctcpMessage); + + static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null ); + + Message(); + Message(const KIRC::Message &obj); + Message(const KIRC::Message *obj); + + ~Message(); + + inline const QString nickFromPrefix() const + { return Kopete::Message::unescape(KIRC::Entity::userNick(m_prefix)); } + + QString toString() const; + + /** \brief Returns true if the message command is numeric. + */ + bool isNumeric() const; + + /** \brief Message is valid if it was parsed correctly. + */ + bool isValid() const; + + /** \brief Writes internal message information about this message through kdDebug(). + */ + void dump() const; + + /** \brief Re-decodes the message with given codec. + */ + void decodeAgain( const QTextCodec *codec ); + + /** \brief The whole message as received. + */ + inline const QCString &raw() const + { return m_raw; } + + /** \brief Prefix of this message. + * + * Returns the prefix of the message. Note that it can be empty. + * + * Prefix is the server name or the nick name of the sender. + * + * message = [ ":" prefix SPACE ] command [ params ] crlf + * prefix = servername / ( nickname [ [ "!" user ] "@" host ] ) + */ + inline const QString &prefix() const + { return m_prefix; } + + /** \brief The command part of this message. + * + * Returns the command of this message. Can be numerical. + * + * Examples: "MODE", "PRIVMSG", 303, 001, ... + */ + inline const QString &command() const + { return m_command; } + + /** \brief The number of command arguments this message contains. + */ + inline size_t argsSize() const + { return m_args.size(); } + + /** \brief i:th command argument. + */ + inline const QString &arg(size_t i) const + { return m_args[i]; } + + /** \brief All command arguments. + */ + inline const QStringList &args() const + { return m_args; } + + /** \brief Message suffix. + */ + inline const QString &suffix() const + { return m_suffix; } + inline const QString &ctcpRaw() const + { return m_ctcpRaw; } + + inline bool hasCtcpMessage() const + { return m_ctcpMessage!=0; } + inline class KIRC::Message &ctcpMessage() const + { return *m_ctcpMessage; } + + static KIRC::Message parse(KIRC::Engine *engine, const QTextCodec *codec, bool *parseSuccess=0); + +private: + /** + * Contains the low level dequoted message. + */ + QCString m_raw; + + /** + * Contains the completely dequoted prefix. + */ + QString m_prefix; + /** + * Contains the completely dequoted command. + */ + QString m_command; + /** + * Contains the completely dequoted args. + */ + QStringList m_args; + /** + * Contains the completely dequoted suffix. + */ + QString m_suffix; + + /** + * If it is a message contains the completely dequoted rawCtcpLine. + * If it is a ctcp message contains the completely dequoted rawCtcpArgsLine. + */ + QString m_ctcpRaw; + + // low level quoting, message quoting + static QString quote(const QString &str); + static QString unquote(const QString &str); + + // ctcp level quoting + static QString ctcpQuote(const QString &str); + static QString ctcpUnquote(const QString &str); + + static bool extractCtcpCommand(QCString &str, QCString &ctcpline); + + static bool matchForIRCRegExp(const QCString &line, const QTextCodec *codec, KIRC::Message &message); + static bool matchForIRCRegExp(QRegExp ®exp, const QTextCodec *codec, const QCString &line, KIRC::Message &message); + + class KIRC::Message *m_ctcpMessage; + + static QRegExp m_IRCCommandType1; + #ifdef _IRC_STRICTNESS_ + static QRegExp m_IRCCommandType2; + #endif // _IRC_STRICTNESS_ + + static QRegExp m_IRCNumericCommand; +}; + +} + +#endif // KIRCMESSAGE_H diff --git a/kopete/protocols/irc/libkirc/kircmessageredirector.cpp b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp new file mode 100644 index 00000000..49194ce0 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp @@ -0,0 +1,97 @@ +/* + kircmessageredirector.cpp - IRC Client + + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 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 "kircengine.h" +#include "kircmessage.h" +#include "kircmessageredirector.h" + +using namespace KIRC; + +MessageRedirector::MessageRedirector(KIRC::Engine *engine, + int argsSize_min, int argsSize_max, const QString &helpMessage) + : QObject(engine, "KIRC::MessageRedirector"), + m_argsSize_min(argsSize_min), + m_argsSize_max(argsSize_max), + m_helpMessage(helpMessage) +{ +} + +bool MessageRedirector::connect(QObject *object, const char *member) +{ + return QObject::connect(this, SIGNAL(redirect(KIRC::Message &)), + object, member); +} + +QStringList MessageRedirector::operator () (Message &msg) +{ + m_errors.clear(); + +// if (m_connectedObjects == 0) +// m_errors.append(i18n("Internal error: no more connected object, triggered by:")+msg); + + if (checkValidity(msg)) + emit redirect(msg); + + return m_errors; +} + +QString MessageRedirector::helpMessage() +{ + return m_helpMessage; +} + +void MessageRedirector::error(QString &message) +{ + m_errors.append(message); +} + +bool MessageRedirector::checkValidity(const Message &msg) +{ + bool success = true; + int argsSize = msg.argsSize(); + + if (m_argsSize_min >= 0 && argsSize < m_argsSize_min) + { +// m_errors.append(i18n("Not enougth arguments in message:")+msg); + success = false; + } + +#ifdef _IRC_STRICTNESS_ + if (m_argsSize_max >= 0 && argsSize > m_argsSize_max) + { +// m_errors.append(i18n("Too many arguments in message:")+msg); + success = false; + } +#endif +/* + if ( msg.isNumeric() && + ( msg.argsSize() > 0 && ( + msg.arg(0) == m_Nickname || + msg.arg(0) == m_PendingNick || + msg.arg(0) == QString::fromLatin1("*") + ) + ) + ) + { +// m_errors.append(i18n("Too many arguments in message:")+msg); + success = false; + } +*/ + return success; +} + +#include "kircmessageredirector.moc" diff --git a/kopete/protocols/irc/libkirc/kircmessageredirector.h b/kopete/protocols/irc/libkirc/kircmessageredirector.h new file mode 100644 index 00000000..f87a2af6 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessageredirector.h @@ -0,0 +1,86 @@ +/* + kircmessageredirector.h - IRC Client + + Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 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. * + * * + ************************************************************************* +*/ + +#ifndef KIRC_MESSAGEREDIRECTOR_H +#define KIRC_MESSAGEREDIRECTOR_H + +#include <qobject.h> +#include <qstring.h> + +namespace KIRC +{ + +class Engine; + +class Message; + +class MessageRedirector + : public QObject +{ + Q_OBJECT + +public: + enum { + Unknown = -1, + Unlimited = -2 + }; + + MessageRedirector(KIRC::Engine *engine, + int argsSize_min = KIRC::MessageRedirector::Unknown, + int argsSize_max = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + /** + * Connects the given object member signal/slot to this message redirector. + * The member signal slot should be looking like: + * SIGNAL(mysignal(KIRC::Message &msg)) + * or + * SIGNAL(myslot(KIRC::Message &msg)) + */ + bool connect(QObject *object, const char *member); + + /** + * Attempt to send the message. + * @return a not empty QStringList on errors or no slots connected. + * The returned string list contains all the errors. + */ + QStringList operator()(KIRC::Message &msg); + + void error(QString &errorMessage); + + QString helpMessage(); + +signals: + void redirect(KIRC::Message &); + +private: + /** + * Check that the given message as the correct number of args + * and do some message format checks. + */ + bool checkValidity(const KIRC::Message &msg); + + QStringList m_errors; + + int m_argsSize_min; + int m_argsSize_max; + QString m_helpMessage; +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/kirctransfer.cpp b/kopete/protocols/irc/libkirc/kirctransfer.cpp new file mode 100644 index 00000000..2466d6a9 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransfer.cpp @@ -0,0 +1,365 @@ +/* + kirctransfer.cpp - IRC transfer. + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003-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 <kdebug.h> +#include <kextsock.h> +#include <klocale.h> + +#include <qfile.h> +#include <qtimer.h> + +#include "kirctransfer.h" + +using namespace KIRC; + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Type type, + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(QString::null), m_fileSize(0), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ +} + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, // put this in a QVariant ? + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ +} + +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + QHostAddress hostAdress, Q_UINT16 port, // put this in a QVariant ? + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, // put this in a QVariant ? + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(0), + m_initiated(false), + m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ + setSocket(new KExtendedSocket(hostAdress.toString(), port)); +} +/* +Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress + Transfer::Type type, QVariant properties, + QObject *parent, const char *name ) + : QObject( parent, name ), + m_engine(engine), m_nick(nick), + m_type(type), m_socket(properties[socket]), + m_initiated(false), + m_file(0), m_fileName(properties[fileName]), m_fileSize(properties[fileSize]), m_fileSizeCur(0), m_fileSizeAck(0), + m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0) +{ + if(!properites["socket"].isNull()) + setSocket(properites["socket"]); + else if(!properites["hostAddress"].isNull() && !properites["hostPort"].isNull()) + setSocket(new KExtendedSocket(properites["hostAddress"], properites["hostPort"])); + + connect(this, SIGNAL(complete()), + this, SLOT(closeSocket())); + + connect(this, SIGNAL(abort(QString)), + this, SLOT(closeSocket())); +} +*/ +Transfer::~Transfer() +{ + closeSocket(); + // m_file is automatically closed on destroy. +} + +Transfer::Status Transfer::status() const +{ + if(m_socket) + { +// return (Transfer::Status)m_socket->socketStatus(); + return Connected; + } + return Error_NoSocket; +} + +void Transfer::slotError( int error ) +{ + // Connection in progress.. This is a signal fired wrong + if (m_socket->socketStatus () != KExtendedSocket::connecting) + { + abort(KExtendedSocket::strError(m_socket->status(), m_socket->systemError())); +// closeSocket(); + } +} + +bool Transfer::initiate() +{ + QTimer *timer = 0; + + if(m_initiated) + { + kdDebug(14121) << k_funcinfo << "Transfer allready initiated" << endl; + return false; + } + + if(!m_socket) + { + kdDebug(14121) << k_funcinfo << "Socket not set" << endl; + return false; + } + + m_initiated = true; + + m_file.setName(m_fileName); + + connect(this, SIGNAL(complete()), + this, SLOT(closeSocket())); + connect(this, SIGNAL(abort(QString)), + this, SLOT(closeSocket())); + +// connect(m_socket, SIGNAL(connectionClosed()), +// this, SLOT(slotConnectionClosed())); +// connect(m_socket, SIGNAL(delayedCloseFinished()), +// this, SLOT(slotConnectionClosed())); + connect(m_socket, SIGNAL(error(int)), // FIXME: connection failed: No such signal KExtendedSocket::error(int) + this, SLOT(slotError(int))); + + switch( m_type ) + { + case Chat: + kdDebug(14121) << k_funcinfo << "Stting up a chat." << endl; + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileIncoming())); + break; + case FileIncoming: + kdDebug(14121) << k_funcinfo << "Stting up an incoming file transfer." << endl; + m_file.open(IO_WriteOnly); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileIncoming())); + break; + case FileOutgoing: + kdDebug(14121) << k_funcinfo << "Stting up an outgoing file transfer." << endl; + m_file.open(IO_ReadOnly); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileOutgoing())); +// timer = new QTimer(this); +// connect(timer, SIGNAL(timeout()), +// this, SLOT(writeFileOutgoing())); +// timer->start(1000, false); + writeFileOutgoing(); // send a first packet. + break; + default: + kdDebug(14121) << k_funcinfo << "Closing transfer: Unknown extra initiation for type:" << m_type << endl; + m_socket->close(); + return false; + break; + } + +// if(status()==Idle) + if(m_socket->status()==KExtendedSocket::nothing) + m_socket->connect(); + + m_socket->enableRead(true); + m_socket->enableWrite(true); + + m_socketDataStream.setDevice(m_socket); + + // I wonder if calling this is really necessary + // As far as I understand, buffer (socket buffer at least) should be flushed while event-looping. + // But I'm not really sure of this, so I force the flush. + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), + this, SLOT(flush())); + timer->start(1000, FALSE); // flush the streams at every seconds + + return true; +} + +bool Transfer::setSocket( KExtendedSocket *socket ) +{ + if (!m_socket) + { + m_socket = socket; + return true; + } + else + kdDebug(14121) << k_funcinfo << "Socket allready set" << endl; + return false; +} + +void Transfer::closeSocket() +{ + if(m_socket) + { + m_socket->close(); +// m_socket->reset(); + m_socket->deleteLater(); + } + m_socket = 0; +} + +/* + * This slot ensure that all the stream are flushed. + * This slot is called periodically internaly. + */ + void Transfer::flush() +{ + /* + * Enure the incoming file content in case of a crash. + */ + if(m_file.isOpen() && m_file.isWritable()) + m_file.flush(); + + /* + * Ensure that non interactive streams outputs (i.e file transfer acknowledge by example) + * are sent (Don't stay in a local buffer). + */ + if(m_socket && status() == Connected) + m_socket->flush(); +} + +void Transfer::userAbort(QString msg) +{ + emit abort(msg); +} + +void Transfer::setCodec( QTextCodec *codec ) +{ + switch( m_type ) + { + case Chat: + m_socket_textStream.setCodec( codec ); + break; + default: +// operation not permitted on this type. + break; + } +} + +void Transfer::writeLine( const QString &line ) +{ + switch( m_type ) + { + case Chat: +// m_socket.flush(); + break; + default: +// operation not permitted on this type. + break; + } +} + +void Transfer::readyReadLine() +{ + if( m_socket->canReadLine() ) + { + QString msg = m_socket_textStream.readLine(); + emit readLine(msg); + } +} + +void Transfer::readyReadFileIncoming() +{ + kdDebug(14121) << k_funcinfo << endl; + + m_bufferLength = m_socket->readBlock(m_buffer, sizeof(m_buffer)); + + if(m_bufferLength > 0) + { + int written = m_file.writeBlock(m_buffer, m_bufferLength); + if(m_bufferLength == written) + { + m_fileSizeCur += written; + m_fileSizeAck = m_fileSizeCur; + m_socketDataStream << m_fileSizeAck; + checkFileTransferEnd(m_fileSizeAck); + return; + } + else + // Something bad happened while writting. + abort(m_file.errorString()); + } + else if(m_bufferLength == -1) + abort("Error while reading socket."); +} + +void Transfer::readyReadFileOutgoing() +{ + kdDebug(14121) << k_funcinfo << "Available bytes:" << m_socket->bytesAvailable() << endl; + + bool hadData = false; + Q_UINT32 fileSizeAck = 0; + +// if (m_socket->bytesAvailable() >= sizeof(fileSizeAck)) // BUGGY: bytesAvailable() that allways return 0 on unbuffered sockets. + { + m_socketDataStream >> fileSizeAck; + hadData = true; + } + + if (hadData) + { + checkFileTransferEnd(fileSizeAck); + writeFileOutgoing(); + } +} + +void Transfer::writeFileOutgoing() +{ + kdDebug(14121) << k_funcinfo << endl; + + if (m_fileSizeAck < m_fileSize) + { + m_bufferLength = m_file.readBlock(m_buffer, sizeof(m_buffer)); + if (m_bufferLength > 0) + { + Q_UINT32 read = m_socket->writeBlock(m_buffer, m_bufferLength); // should check written == read + +// if(read != m_buffer_length) +// buffer is not cleared still + + m_fileSizeCur += read; +// m_socket->flush(); // Should think on using this + emit fileSizeCurrent( m_fileSizeCur ); + } + else if(m_bufferLength == -1) + abort("Error while reading file."); + } +} + +void Transfer::checkFileTransferEnd(Q_UINT32 fileSizeAck) +{ + kdDebug(14121) << k_funcinfo << "Acknowledged:" << fileSizeAck << endl; + + m_fileSizeAck = fileSizeAck; + emit fileSizeAcknowledge(m_fileSizeAck); + + if(m_fileSizeAck > m_fileSize) + abort(i18n("Acknowledge size is greater than the expected file size")); + + if(m_fileSizeAck == m_fileSize) + emit complete(); +} + +#include "kirctransfer.moc" diff --git a/kopete/protocols/irc/libkirc/kirctransfer.h b/kopete/protocols/irc/libkirc/kirctransfer.h new file mode 100644 index 00000000..3453f5cb --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransfer.h @@ -0,0 +1,191 @@ +/* + kirctransfer.h - DCC Handler + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCTRANSFER_H +#define KIRCTRANSFER_H + +#include <qdatastream.h> +#include <qfile.h> +#include <qhostaddress.h> +#include <qobject.h> +#include <qtextstream.h> + +class KExtendedSocket; + +class QFile; +class QTextCodec; + +namespace KIRC +{ +class Engine; + +class Transfer + : public QObject +{ + Q_OBJECT + +public: + enum Type { + Unknown, + Chat, + FileOutgoing, + FileIncoming + }; + + enum Status { + Error_NoSocket = -2, + Error = -1, + Idle = 0, + HostLookup, + Connecting, + Connected, + Closed + }; +public: + Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress + Type type = Unknown, + QObject *parent = 0L, const char *name = 0L ); + + Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress, + QHostAddress peer_address, Q_UINT16 peer_port, + Transfer::Type type, + QObject *parent = 0L, const char *name = 0L ); + + Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, + QObject *parent = 0L, const char *name = 0L ); + + Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress, + QHostAddress peer_address, Q_UINT16 peer_port, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, + QObject *parent = 0L, const char *name = 0L ); +/* + For a file transfer properties are: + + KExntendedSocket *socket + or + QHostAddress peerAddress + Q_UINT16 peerPort + for determining the socket. + + QString fileName + Q_UINT32 fileSize + for detemining the file propeties. +*//* + Transfer( KIRC *engine, QString nick,// QString nick_peer_adress, + Transfer::Type type, QVariant properties, + QObject *parent = 0L, const char *name = 0L ); +*/ + ~Transfer(); + + KIRC::Engine *engine() const + { return m_engine; } + QString nick() const + { return m_nick; } + Type type() const + { return m_type; } + Status status() const; + + /* Start the transfer. + * If not connected connect to client. + * Allow receiving/emitting data. + */ + bool initiate(); + + QString fileName() const + { return m_fileName; } + /* Change the file name. + */ + void setFileName(QString fileName) + { m_fileName = fileName; } + unsigned long fileSize() const + { return m_fileSize; } +public slots: + bool setSocket( KExtendedSocket *socket ); + void closeSocket(); + + void setCodec( QTextCodec *codec ); + void writeLine( const QString &msg ); + + void flush(); + + void userAbort(QString); + +signals: + void readLine( const QString &msg ); + + void fileSizeCurrent( unsigned int ); + void fileSizeAcknowledge( unsigned int ); + +// void received(Q_UINT32); +// void sent(Q_UINT32); + + void abort(QString); + + /* Emited when the transfer is complete. + * Usually it means that the file transfer has successfully finished. + */ + void complete(); + +protected slots: + void slotError(int); + + void readyReadLine(); + + void readyReadFileIncoming(); + + void writeFileOutgoing(); + void readyReadFileOutgoing(); + +protected: +// void emitSignals(); + void checkFileTransferEnd( Q_UINT32 fileSizeAck ); + + KIRC::Engine * m_engine; + QString m_nick; + + Type m_type; + KExtendedSocket *m_socket; + bool m_initiated; + + // Text member data + QTextStream m_socket_textStream; +// QTextCodec * m_socket_codec; + + // File member data + QFile m_file; + QString m_fileName; + Q_UINT32 m_fileSize; + Q_UINT32 /*usize_t*/ m_fileSizeCur; + Q_UINT32 /*usize_t*/ m_fileSizeAck; + QDataStream m_socketDataStream; + char m_buffer[1024]; + int m_bufferLength; + + // Data transfer measures + Q_UINT32 m_receivedBytes; + Q_UINT32 m_receivedBytesLimit; + + Q_UINT32 m_sentBytes; + Q_UINT32 m_sentBytesLimit; +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/kirctransferhandler.cpp b/kopete/protocols/irc/libkirc/kirctransferhandler.cpp new file mode 100644 index 00000000..3fa73dff --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransferhandler.cpp @@ -0,0 +1,97 @@ +/* + kirctransferhandler.cpp - DCC Handler + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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 <kglobal.h> +#include <klocale.h> +#include <kextsock.h> + +#include <qfile.h> +#include <qregexp.h> +#include <qtextcodec.h> + +#include "kirctransferserver.h" + +#include "kirctransferhandler.h" + +using namespace KIRC; + +TransferHandler *TransferHandler::self() +{ + static TransferHandler sm_self; + return &sm_self; +} + +TransferServer *TransferHandler::server() +{ + if( m_server ) +// server( m_default_server_port, m_default_server_backlog ); + server( 0, 1 ); + return m_server; +} + +TransferServer *TransferHandler::server( Q_UINT16 port, int backlog ) +{ +// if( m_server ) +// m_server->terminate(); + TransferServer *m_server = new TransferServer( port, backlog, this ); + + // here connect the slots of the server + + return m_server; +} + +TransferServer *TransferHandler::createServer(Engine *engine, QString m_userName, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize) +{ + TransferServer *server = new TransferServer(engine, m_userName, type, fileName, fileSize, this); + transferServerCreated(server); + return server; +} + +Transfer *TransferHandler::createClient( + Engine *engine, QString nick,// QString nick_peer_adress, + QHostAddress peer_address, Q_UINT16 peer_port, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize ) +{ + Transfer *client = new Transfer( + engine, nick,// QString nick_peer_adress, + peer_address, peer_port, + type, + fileName, fileSize, + this ); + transferCreated(client); + return client; +} + +/* +File *DCCHandler::openFile( QString file, int mode = IO_ReadWrite ) +{ + QFile *file = new QFile(filename); + if (!file->open(mode)) + { + delete file; + file = 0L; + } + return file; +} +*/ + +#include "kirctransferhandler.moc" + +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/protocols/irc/libkirc/kirctransferhandler.h b/kopete/protocols/irc/libkirc/kirctransferhandler.h new file mode 100644 index 00000000..81774c02 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransferhandler.h @@ -0,0 +1,79 @@ +/* + kirctransferhandler.h - DCC Handler + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003-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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCTRANSFERHANDLER_H +#define KIRCTRANSFERHANDLER_H + +#include <qhostaddress.h> + +#include "kirctransfer.h" +#include "kirctransferserver.h" + +class QFile; +class QTextCodec; + +class KExtendedSocket; + +namespace KIRC +{ + +class TransferHandler + : public QObject +{ + Q_OBJECT + +public: + static TransferHandler *self(); + + TransferServer *server(); + TransferServer *server( Q_UINT16 port, int backlog = 1 ); + + TransferServer *createServer(KIRC::Engine *engine, QString m_userName, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize); + + Transfer *createClient( + KIRC::Engine *engine, QString nick,// QString nick_peer_adress, + QHostAddress peer_address, Q_UINT16 peer_port, + Transfer::Type type, + QString file = QString::null, Q_UINT32 fileSize = 0 ); + +// void registerServer( DCCServer * ); +// QPtrList<DCCServer> getRegisteredServers(); +// static QPtrList<DCCServer> getAllRegisteredServers(); +// void unregisterServer( DCCServer * ); + +// void registerClient( DCCClient * ); +// QPtrList<DCCClient> getRegisteredClients(); +// static QPtrList<DCCClient> getAllRegisteredClients(); +// void unregisterClient( DCCClient * ); + +signals: + void transferServerCreated(KIRC::TransferServer *server); + void transferCreated(KIRC::Transfer *transfer); + +private: +// TransferHandler(); + + TransferServer *m_server; +// QPtrList<TransferServer> m_servers; +// QPtrList<Transfer> m_clients; +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/kirctransferserver.cpp b/kopete/protocols/irc/libkirc/kirctransferserver.cpp new file mode 100644 index 00000000..96cc66fb --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransferserver.cpp @@ -0,0 +1,154 @@ +/* + kirctransfer.cpp - IRC transfer. + + Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003 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 <kdebug.h> +#include <kextsock.h> + +#include "kirctransferhandler.h" + +#include "kirctransferserver.h" + +using namespace KIRC; + +/* +TransferServer::TransferServer( QObject *parent, const char *name ) + : QObject( parent, name ), + m_socket( 0 ), + m_port( 0 ), + m_backlog( 1 ) +{ +} +*/ +TransferServer::TransferServer(Q_UINT16 port, int backlog, QObject *parent, const char *name) + : QObject( parent, name ), + m_socket( 0 ), + m_port( port ), + m_backlog( backlog ) +{ +} + +TransferServer::TransferServer(Engine *engine, QString nick,// QString nick_peer_adress, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, + QObject *parent, const char *name) + : QObject( parent, name ), + m_socket(0), + m_port(0), + m_backlog(1), + m_engine(engine), + m_nick(nick), + m_type(type), + m_fileName(fileName), + m_fileSize(fileSize) +{ + initServer(); +} + +TransferServer::~TransferServer() +{ + if (m_socket) + delete m_socket; +} + +bool TransferServer::initServer() +{ + if (!m_socket) + { + QObject::connect(this, SIGNAL(incomingNewTransfer(Transfer *)), + TransferHandler::self(), SIGNAL(transferCreated(Transfer *))); + + m_socket = new KExtendedSocket(); + +// m_socket->setHost(m_socket->localAddress()->nodeName()); + if (!m_socket->setPort(m_port)) + kdDebug(14120) << k_funcinfo << "Failed to set port to" << m_port << endl; + m_socket->setSocketFlags(KExtendedSocket::noResolve + |KExtendedSocket::passiveSocket + |KExtendedSocket::inetSocket ); + + if (!m_socket->setTimeout(2*60)) // FIXME: allow configuration of this. + kdDebug(14120) << k_funcinfo << "Failed to set timeout." << endl; + + QObject::connect(m_socket, SIGNAL(readyAccept()), + this, SLOT(readyAccept())); + QObject::connect(m_socket, SIGNAL(connectionFailed(int)), + this, SLOT(connectionFailed(int))); + + m_socket->listen(m_backlog); + m_socket->setBlockingMode(true); + + const KInetSocketAddress *localAddress = static_cast<const KInetSocketAddress *>(m_socket->localAddress()); + if (!localAddress) + { + kdDebug(14120) << k_funcinfo << "Not a KInetSocketAddress." << endl; + deleteLater(); + return false; + } + + m_port = localAddress->port(); + } + return (m_socket->socketStatus() != KExtendedSocket::error); +} + +bool TransferServer::initServer( Q_UINT16 port, int backlog ) +{ + if (m_socket) + { + m_port = port; + m_backlog = backlog; + } + return initServer(); +} + +void TransferServer::readyAccept() +{ + KExtendedSocket *socket; + m_socket->accept( socket ); + Transfer *transfer = new Transfer(m_engine, m_nick, m_type, m_fileName, m_fileSize); + transfer->setSocket(socket); + transfer->initiate(); + emit incomingNewTransfer(transfer); +} + +void TransferServer::connectionFailed(int error) +{ + if (error!=0) + { + kdDebug(14120) << k_funcinfo << "Connection failed with " << m_nick << endl; + deleteLater(); + } +} +/* +void Transfer::initClient() +{ + if(!m_socket) + { + connect(m_socket, SIGNAL(connectionClosed()), + this, SLOT(slotConnectionClosed())); + connect(m_socket, SIGNAL(delayedCloseFinished()), + this, SLOT(slotConnectionClosed())); + connect(m_socket, SIGNAL(error(int)), + this, SLOT(slotError(int))); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readyReadFileOut)); + + m_socket->enableRead( true ); + m_socket->enableWrite( true ); + } +} +*/ +#include "kirctransferserver.moc" diff --git a/kopete/protocols/irc/libkirc/kirctransferserver.h b/kopete/protocols/irc/libkirc/kirctransferserver.h new file mode 100644 index 00000000..8ac016ef --- /dev/null +++ b/kopete/protocols/irc/libkirc/kirctransferserver.h @@ -0,0 +1,81 @@ +/* + kirctransfer.h - DCC Handler + + Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2003-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. * + * * + ************************************************************************* +*/ + +#ifndef KIRCTRANSFERSERVER_H +#define KIRCTRANSFERSERVER_H + +#include "kirctransfer.h" + +#include <qobject.h> + +class KExtendedSocket; + +class QFile; +class QTextCodec; + +namespace KIRC +{ + +class TransferServer + : public QObject +{ + Q_OBJECT + +public: +// TransferServer(QObject *parent = 0, const char *name = 0); + TransferServer(Q_UINT16 port, int backlog = 1, QObject *parent = 0, const char *name = 0); + TransferServer(KIRC::Engine *engine, QString nick,// QString nick_peer_adress, + Transfer::Type type, + QString fileName, Q_UINT32 fileSize, + QObject *parent = 0, const char *name = 0); + + ~TransferServer(); + + int port() + { return m_port; } + +protected: + bool initServer(); + bool initServer( Q_UINT16 port, int backlog = 1 ); + +signals: + void incomingNewTransfer(Transfer *transfer); + +protected slots: + void readyAccept(); + void connectionFailed(int error); + +private: + KExtendedSocket * m_socket; + Q_UINT16 m_port; + int m_backlog; + + // The following will be deprecated ... + KIRC::Engine * m_engine; + QString m_nick; + Transfer::Type m_type; + QString m_fileName; + Q_UINT32 m_fileSize; + // by + // QPtrList<Transfer> m_pendingTransfers; + // QPtrList<Transfer> m_activeTransfers; + +}; + +} + +#endif diff --git a/kopete/protocols/irc/libkirc/ksslsocket.cpp b/kopete/protocols/irc/libkirc/ksslsocket.cpp new file mode 100644 index 00000000..fb2d5161 --- /dev/null +++ b/kopete/protocols/irc/libkirc/ksslsocket.cpp @@ -0,0 +1,458 @@ +/* + ksslsocket.cpp - KDE SSL Socket + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2005 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 <qsocketnotifier.h> + +#include <dcopclient.h> +#include <klocale.h> +#include <kdebug.h> +#include <kssl.h> +#include <ksslinfodlg.h> +#include <ksslpeerinfo.h> +#include <ksslcertchain.h> +#include <ksslcertificatecache.h> +#include <kapplication.h> +#include <kmessagebox.h> + +#include "ksslsocket.h" + +struct KSSLSocketPrivate +{ + mutable KSSL *kssl; + KSSLCertificateCache *cc; + DCOPClient *dcc; + QMap<QString,QString> metaData; + QSocketNotifier *socketNotifier; +}; + +KSSLSocket::KSSLSocket() : KExtendedSocket() +{ + d = new KSSLSocketPrivate; + d->kssl = 0; + d->dcc = KApplication::kApplication()->dcopClient(); + d->cc = new KSSLCertificateCache; + d->cc->reload(); + + //No blocking + setBlockingMode(false); + + //Connect internal slots + QObject::connect( this, SIGNAL(connectionSuccess()), this, SLOT(slotConnected()) ); + QObject::connect( this, SIGNAL(closed(int)), this, SLOT(slotDisconnected()) ); + QObject::connect( this, SIGNAL(connectionFailed(int)), this, SLOT(slotDisconnected())); +} + +KSSLSocket::~KSSLSocket() +{ + //Close connection + closeNow(); + + if( d->kssl ) + { + d->kssl->close(); + delete d->kssl; + } + + delete d->cc; + + delete d; +} + +Q_LONG KSSLSocket::readBlock( char* data, Q_ULONG maxLen ) +{ + //Re-implemented because KExtSocket doesn't use this when not in buffered mode + Q_LONG retval = consumeReadBuffer(maxLen, data); + + if( retval == 0 ) + { + if (sockfd == -1) + return 0; + + retval = -1; + } + + return retval; +} + +int KSSLSocket::peekBlock( char* data, uint maxLen ) +{ + //Re-implemented because KExtSocket doesn't use this when not in buffered mode + if( socketStatus() < connected ) + return -2; + + if( sockfd == -1 ) + return -2; + + return consumeReadBuffer(maxLen, data, false); +} + +Q_LONG KSSLSocket::writeBlock( const char* data, Q_ULONG len ) +{ + return d->kssl->write( data, len ); +} + +int KSSLSocket::bytesAvailable() const +{ + if( socketStatus() < connected ) + return -2; + + //Re-implemented because KExtSocket doesn't use this when not in buffered mode + return KBufferedIO::bytesAvailable(); +} + +void KSSLSocket::slotReadData() +{ + kdDebug(14120) << k_funcinfo << d->kssl->pending() << endl; + QByteArray buff(512); + int bytesRead = d->kssl->read( buff.data(), 512 ); + + //Fill the read buffer + feedReadBuffer( bytesRead, buff.data() ); + emit readyRead(); +} + +void KSSLSocket::slotConnected() +{ + if (!KSSL::doesSSLWork()) { + kdError(14120) << k_funcinfo << "SSL not functional!" << endl; + + closeNow(); + emit sslFailure(); + return; + } + + delete d->kssl; + d->kssl = new KSSL(); + + if (d->kssl->connect( sockfd ) != 1) { + kdError(14120) << k_funcinfo << "SSL connect() failed." << endl; + + closeNow(); + emit sslFailure(); + return; + } + + //Disconnect the KExtSocket notifier slot, we use our own + QObject::disconnect( readNotifier(), SIGNAL(activated( int )), + this, SLOT(socketActivityRead()) ); + + QObject::connect( readNotifier(), SIGNAL(activated( int )), + this, SLOT(slotReadData()) ); + + readNotifier()->setEnabled(true); + + if (verifyCertificate() != 1) { + closeNow(); + emit certificateRejected(); + return; + } + + emit certificateAccepted(); +} + +void KSSLSocket::slotDisconnected() +{ + kdDebug(14120) << k_funcinfo << "Disconnected" << endl; + + if( readNotifier() ) + readNotifier()->setEnabled(false); + + delete d->kssl; + d->kssl = 0L; +} + +void KSSLSocket::showInfoDialog() +{ + if( socketStatus() == connected ) + { + if (!d->dcc->isApplicationRegistered("kio_uiserver")) + { + KApplication::startServiceByDesktopPath("kio_uiserver.desktop",QStringList()); + } + + QByteArray data, ignore; + QCString ignoretype; + QDataStream arg(data, IO_WriteOnly); + arg << "irc://" + peerAddress()->pretty() + ":" + port() << d->metaData; + d->dcc->call("kio_uiserver", "UIServer", + "showSSLInfoDialog(QString,KIO::MetaData)", data, ignoretype, ignore); + } +} + +void KSSLSocket::setMetaData( const QString &key, const QVariant &data ) +{ + QVariant v = data; + d->metaData[key] = v.asString(); +} + +bool KSSLSocket::hasMetaData( const QString &key ) +{ + return d->metaData.contains(key); +} + +QString KSSLSocket::metaData( const QString &key ) +{ + if( d->metaData.contains(key) ) + return d->metaData[key]; + return QString::null; +} + +/* +I basically copied the below from tcpKIO::SlaveBase.hpp, with some modificaions and formatting. + + * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net + * Copyright (C) 2001-2003 George Staikos <staikos@kde.org> + * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org> +*/ + +int KSSLSocket::messageBox( KIO::SlaveBase::MessageBoxType type, const QString &text, const QString &caption, + const QString &buttonYes, const QString &buttonNo ) +{ + kdDebug(14120) << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo << endl; + QByteArray data, result; + QCString returnType; + QDataStream arg(data, IO_WriteOnly); + arg << (int)1 << (int)type << text << caption << buttonYes << buttonNo; + + if (!d->dcc->isApplicationRegistered("kio_uiserver")) + { + KApplication::startServiceByDesktopPath("kio_uiserver.desktop",QStringList()); + } + + d->dcc->call("kio_uiserver", "UIServer", + "messageBox(int,int,QString,QString,QString,QString)", data, returnType, result); + + if( returnType == "int" ) + { + int res; + QDataStream r(result, IO_ReadOnly); + r >> res; + return res; + } + else + return 0; // communication failure +} + + +// Returns 0 for failed verification, -1 for rejected cert and 1 for ok +int KSSLSocket::verifyCertificate() +{ + int rc = 0; + bool permacache = false; + bool _IPmatchesCN = false; + int result; + bool doAddHost = false; + QString ourHost = host(); + QString ourIp = peerAddress()->pretty(); + + QString theurl = "irc://" + ourHost + ":" + port(); + + if (!d->cc) + d->cc = new KSSLCertificateCache; + + KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate(); + + KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer); + + _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress(); + + if (!_IPmatchesCN) + { + ksvl << KSSLCertificate::InvalidHost; + } + + KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok; + if (!ksvl.isEmpty()) + ksv = ksvl.first(); + + /* Setting the various bits of meta-info that will be needed. */ + setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher()); + setMetaData("ssl_cipher_desc", d->kssl->connectionInfo().getCipherDescription()); + setMetaData("ssl_cipher_version", d->kssl->connectionInfo().getCipherVersion()); + setMetaData("ssl_cipher_used_bits", QString::number(d->kssl->connectionInfo().getCipherUsedBits())); + setMetaData("ssl_cipher_bits", QString::number(d->kssl->connectionInfo().getCipherBits())); + setMetaData("ssl_peer_ip", ourIp ); + + QString errorStr; + for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin(); + it != ksvl.end(); ++it) + { + errorStr += QString::number(*it)+":"; + } + + setMetaData("ssl_cert_errors", errorStr); + setMetaData("ssl_peer_certificate", pc.toString()); + + if (pc.chain().isValid() && pc.chain().depth() > 1) + { + QString theChain; + QPtrList<KSSLCertificate> chain = pc.chain().getChain(); + for (KSSLCertificate *c = chain.first(); c; c = chain.next()) + { + theChain += c->toString(); + theChain += "\n"; + } + setMetaData("ssl_peer_chain", theChain); + } + else + { + setMetaData("ssl_peer_chain", ""); + } + + setMetaData("ssl_cert_state", QString::number(ksv)); + + if (ksv == KSSLCertificate::Ok) + { + rc = 1; + setMetaData("ssl_action", "accept"); + } + + // Since we're the parent, we need to teach the child. + setMetaData("ssl_parent_ip", ourIp ); + setMetaData("ssl_parent_cert", pc.toString()); + + // - Read from cache and see if there is a policy for this + KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(pc); + + // - validation code + if (ksv != KSSLCertificate::Ok) + { + if( cp == KSSLCertificateCache::Unknown || cp == KSSLCertificateCache::Ambiguous) + { + cp = KSSLCertificateCache::Prompt; + } + else + { + // A policy was already set so let's honor that. + permacache = d->cc->isPermanent(pc); + } + + if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) + { + cp = KSSLCertificateCache::Prompt; + } + + // Precondition: cp is one of Reject, Accept or Prompt + switch (cp) + { + case KSSLCertificateCache::Accept: + rc = 1; + break; + + case KSSLCertificateCache::Reject: + rc = -1; + break; + + case KSSLCertificateCache::Prompt: + { + do + { + if (ksv == KSSLCertificate::InvalidHost) + { + QString msg = i18n("The IP address of the host %1 " + "does not match the one the " + "certificate was issued to."); + result = messageBox( KIO::SlaveBase::WarningYesNoCancel, + msg.arg(ourHost), + i18n("Server Authentication"), + i18n("&Details"), + i18n("Co&ntinue") ); + } + else + { + QString msg = i18n("The server certificate failed the " + "authenticity test (%1)."); + result = messageBox( KIO::SlaveBase::WarningYesNoCancel, + msg.arg(ourHost), + i18n("Server Authentication"), + i18n("&Details"), + i18n("Co&ntinue") ); + } + + if (result == KMessageBox::Yes) + { + showInfoDialog(); + } + } + while (result == KMessageBox::Yes); + + if (result == KMessageBox::No) + { + rc = 1; + cp = KSSLCertificateCache::Accept; + doAddHost = true; + result = messageBox( KIO::SlaveBase::WarningYesNo, + i18n("Would you like to accept this " + "certificate forever without " + "being prompted?"), + i18n("Server Authentication"), + i18n("&Forever"), + i18n("&Current Sessions Only")); + if (result == KMessageBox::Yes) + permacache = true; + else + permacache = false; + } + else + { + rc = -1; + cp = KSSLCertificateCache::Prompt; + } + + break; + } + default: + kdDebug(14120) << "SSL error in cert code." + << "Please report this to kopete-devel@kde.org." + << endl; + break; + } + } + + // - cache the results + d->cc->addCertificate(pc, cp, permacache); + if (doAddHost) + d->cc->addHost(pc, ourHost); + + + if (rc == -1) + return rc; + + + kdDebug(14120) << "SSL connection information follows:" << endl + << "+-----------------------------------------------" << endl + << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl + << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl + << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl + << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits() + << " of " << d->kssl->connectionInfo().getCipherBits() + << " bits used." << endl + << "| PEER:" << endl + << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl + << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl + << "| Validation: " << (int)ksv << endl + << "| Certificate matches IP: " << _IPmatchesCN << endl + << "+-----------------------------------------------" + << endl; + + // sendMetaData(); Do not call this function!! + return rc; +} + + +#include "ksslsocket.moc" diff --git a/kopete/protocols/irc/libkirc/ksslsocket.h b/kopete/protocols/irc/libkirc/ksslsocket.h new file mode 100644 index 00000000..692d5288 --- /dev/null +++ b/kopete/protocols/irc/libkirc/ksslsocket.h @@ -0,0 +1,68 @@ + +#ifndef _K_SSL_SOCKET_H_ +#define _K_SSL_SOCKET_H_ + +/* + ksslsocket.h - KDE SSL Socket + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 2002-2005 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 <qvariant.h> +#include <kextsock.h> +#include <kio/slavebase.h> + +class KSSLSocketPrivate; + +class KSSLSocket : public KExtendedSocket +{ + Q_OBJECT + + public: + KSSLSocket(); + ~KSSLSocket(); + + Q_LONG readBlock( char* data, Q_ULONG maxLen ); + Q_LONG writeBlock( const char* data, Q_ULONG len ); + int peekBlock( char *data, uint maxLen ); + int bytesAvailable() const; + + void showInfoDialog(); + + signals: + void sslFailure(); + void certificateAccepted(); + void certificateRejected(); + + private slots: + void slotConnected(); + void slotDisconnected(); + void slotReadData(); + + private: + int verifyCertificate(); + int messageBox( KIO::SlaveBase::MessageBoxType type, const QString &text, + const QString &caption, const QString &buttonYes, const QString &buttonNo ); + + + //Copied frm tcpslavebase to simply integrating their dialog function + void setMetaData( const QString &, const QVariant & ); + bool hasMetaData( const QString & ); + QString metaData( const QString & ); + + KSSLSocketPrivate *d; +}; + +#endif diff --git a/kopete/protocols/irc/ui/Makefile.am b/kopete/protocols/irc/ui/Makefile.am new file mode 100644 index 00000000..854a7398 --- /dev/null +++ b/kopete/protocols/irc/ui/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO + +noinst_LTLIBRARIES = libkopeteircui.la +AM_CPPFLAGS = $(KOPETE_INCLUDES) \ + -I$(srcdir)/..\ + -I$(srcdir)/../libkirc \ + $(all_includes) + + +libkopeteircui_la_SOURCES = ircadd.ui empty.cpp irceditaccountwidget.cpp \ + irceditaccount.ui channellist.cpp channellistdialog.cpp networkconfig.ui +EXTRA_DIST = ircadd.ui ircprefs.ui empty.cpp diff --git a/kopete/protocols/irc/ui/channellist.cpp b/kopete/protocols/irc/ui/channellist.cpp new file mode 100644 index 00000000..5c66ede0 --- /dev/null +++ b/kopete/protocols/irc/ui/channellist.cpp @@ -0,0 +1,346 @@ +/* + channellist.cpp - IRC Channel Search Widget + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.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 "channellist.h" + +#include "kircengine.h" + +#include <klocale.h> +#include <kmessagebox.h> + +#include <qvariant.h> +#include <qlabel.h> +#include <qpainter.h> +#include <qapplication.h> +#include <qsimplerichtext.h> +#include <qstyle.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qheader.h> +#include <klistview.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qspinbox.h> +#include <qwhatsthis.h> + +class ChannelListItem : public KListViewItem +{ + public: + ChannelListItem( KListView *parent, QString arg1, QString arg2, QString arg3 ); + virtual int compare( QListViewItem *i, int col, bool ascending ) const; + virtual void paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align ); + + private: + KListView *parentList; +}; + +ChannelListItem::ChannelListItem( KListView *parent, QString arg1, QString arg2, QString arg3 ) : + KListViewItem( parent, parent->lastItem() ), parentList( parent ) +{ + setText(0, arg1); + setText(1, arg2); + setText(2, arg3); +} + +int ChannelListItem::compare( QListViewItem *i, int col, bool ascending ) const +{ + if( col == 1 ) + { + if( text(1).toUInt() < i->text(1).toUInt() ) + return -1; + else if ( text(1).toUInt() == i->text(1).toUInt() ) + return 0; + else + return 1; + } + else + return QListViewItem::compare( i, col, ascending ); +} + +void ChannelListItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align ) +{ + QPixmap back( width, height() ); + QPainter paint( &back ); + //KListViewItem::paintCell( &paint, cg, column, width, align ); + // PASTED FROM KLISTVIEWITEM: + // set the alternate cell background colour if necessary + QColorGroup _cg = cg; + if (isAlternate()) + if (listView()->viewport()->backgroundMode()==Qt::FixedColor) + _cg.setColor(QColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground()); + else + _cg.setColor(QColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground()); + // PASTED FROM QLISTVIEWITEM + { + QPainter *p = &paint; + + QListView *lv = listView(); + if ( !lv ) + return; + QFontMetrics fm( p->fontMetrics() ); + + // any text we render is done by the Components, not by this class, so make sure we've nothing to write + QString t; + + // removed text truncating code from Qt - we do that differently, further on + + int marg = lv->itemMargin(); + int r = marg; + // const QPixmap * icon = pixmap( column ); + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode ); + + if ( _cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), _cg.brush( crole ) ); + else + { + // all copied from QListView::paintEmptyArea + + //lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + QStyleOption opt( lv->sortColumn(), 0 ); // ### hack; in 3.1, add a property in QListView and QHeader + QStyle::SFlags how = QStyle::Style_Default; + if ( lv->isEnabled() ) + how |= QStyle::Style_Enabled; + + lv->style().drawComplexControl( QStyle::CC_ListView, + p, lv, QRect( 0, 0, width, height() ), lv->colorGroup(), + how, QStyle::SC_ListView, QStyle::SC_None, + opt ); + } + + + + if ( isSelected() && + (column == 0 || lv->allColumnsShowFocus()) ) { + p->fillRect( r - marg, 0, width - r + marg, height(), + _cg.brush( QColorGroup::Highlight ) ); + // removed text pen setting code from Qt + } + + // removed icon drawing code from Qt + + // draw the tree gubbins + if ( multiLinesEnabled() && column == 0 && isOpen() && childCount() ) { + int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin(); + textheight = QMAX( textheight, QApplication::globalStrut().height() ); + if ( textheight % 2 > 0 ) + textheight++; + if ( textheight < height() ) { + int w = lv->treeStepSize() / 2; + lv->style().drawComplexControl( QStyle::CC_ListView, p, lv, + QRect( 0, textheight, w + 1, height() - textheight + 1 ), _cg, + lv->isEnabled() ? QStyle::Style_Enabled : QStyle::Style_Default, + QStyle::SC_ListViewExpand, + (uint)QStyle::SC_All, QStyleOption( this ) ); + } + } + } + // END OF PASTE + + + //do you see a better way to tell the TextComponent we are selected ? - Olivier 2004-09-02 + if ( isSelected() ) + _cg.setColor(QColorGroup::Text , _cg.highlightedText() ); + + QSimpleRichText myrichtext( text(column), paint.font() ); + myrichtext.draw( &paint, 0, 0, paint.window(), _cg ); + + paint.end(); + p->drawPixmap( 0, 0, back ); +} + +ChannelList::ChannelList( QWidget* parent, KIRC::Engine *engine ) + : QWidget( parent ), m_engine( engine ) +{ + ChannelListLayout = new QVBoxLayout( this, 11, 6, "ChannelListLayout"); + + layout72_2 = new QHBoxLayout( 0, 0, 6, "layout72_2"); + + textLabel1_2 = new QLabel( this, "textLabel1_2" ); + layout72_2->addWidget( textLabel1_2 ); + + channelSearch = new QLineEdit( this, "channelSearch" ); + layout72_2->addWidget( channelSearch ); + + numUsers = new QSpinBox( 0, 32767, 1, this, "num_users" ); + numUsers->setSuffix( i18n(" members") ); + layout72_2->addWidget( numUsers ); + + mSearchButton = new QPushButton( this, "mSearchButton" ); + layout72_2->addWidget( mSearchButton ); + ChannelListLayout->addLayout( layout72_2 ); + + mChannelList = new KListView( this, "mChannelList" ); + mChannelList->addColumn( i18n( "Channel" ) ); + mChannelList->addColumn( i18n( "Users" ) ); + mChannelList->header()->setResizeEnabled( FALSE, mChannelList->header()->count() - 1 ); + mChannelList->addColumn( i18n( "Topic" ) ); + mChannelList->setAllColumnsShowFocus( TRUE ); + mChannelList->setShowSortIndicator( TRUE ); + ChannelListLayout->addWidget( mChannelList ); + + clearWState( WState_Polished ); + + textLabel1_2->setText( i18n( "Search for:" ) ); + QToolTip::add( textLabel1_2, i18n( "You may search for channels on the IRC server for a text string entered here." ) ); + QToolTip::add( numUsers, i18n( "Channels returned must have at least this many members." ) ); + QWhatsThis::add( numUsers, i18n( "Channels returned must have at least this many members." ) ); + QWhatsThis::add( textLabel1_2, i18n( "You may search for channels on the IRC server for a text string entered here. For instance, you may type 'linux' to find channels that have something to do with linux." ) ); + QToolTip::add( channelSearch, i18n( "You may search for channels on the IRC server for a text string entered here." ) ); + QWhatsThis::add( channelSearch, i18n( "You may search for channels on the IRC server for a text string entered here. For instance, you may type 'linux' to find channels that have something to do with linux." ) ); + mSearchButton->setText( i18n( "S&earch" ) ); + QToolTip::add( mSearchButton, i18n( "Perform a channel search." ) ); + QWhatsThis::add( mSearchButton, i18n( "Perform a channel search. Please be patient, as this can be slow depending on the number of channels on the server." ) ); + QToolTip::add( mChannelList, i18n( "Double click on a channel to select it." ) ); + mChannelList->header()->setLabel( 0, i18n( "Channel" ) ); + mChannelList->header()->setLabel( 1, i18n( "Users" ) ); + mChannelList->header()->setLabel( 2, i18n( "Topic" ) ); + + // signals and slots connections + connect( mChannelList, SIGNAL( doubleClicked(QListViewItem*) ), + this, SLOT( slotItemDoubleClicked(QListViewItem*) ) ); + + connect( mSearchButton, SIGNAL( clicked() ), this, SLOT( search() ) ); + + connect( mChannelList, SIGNAL( selectionChanged( QListViewItem*) ), this, + SLOT( slotItemSelected( QListViewItem *) ) ); + + connect( m_engine, SIGNAL( incomingListedChan( const QString &, uint, const QString & ) ), + this, SLOT( slotChannelListed( const QString &, uint, const QString & ) ) ); + + connect( m_engine, SIGNAL( incomingEndOfList() ), this, SLOT( slotListEnd() ) ); + + connect( m_engine, SIGNAL( statusChanged(KIRC::Engine::Status) ), + this, SLOT( slotStatusChanged(KIRC::Engine::Status) ) ); + + show(); +} + +void ChannelList::slotItemDoubleClicked( QListViewItem *i ) +{ + emit channelDoubleClicked( i->text(0) ); +} + +void ChannelList::slotItemSelected( QListViewItem *i ) +{ + emit channelSelected( i->text(0) ); +} + +void ChannelList::slotStatusChanged(KIRC::Engine::Status newStatus) +{ + switch(newStatus) { + case KIRC::Engine::Connected: + this->reset(); + break; + case KIRC::Engine::Disconnected: + if (mSearching) { + KMessageBox::queuedMessageBox( + this, KMessageBox::Error, + i18n("You have been disconnected from the IRC server."), + i18n("Disconnected"), 0 + ); + } + + slotListEnd(); + break; + default: + break; + } +} + +void ChannelList::reset() +{ + channelCache.clear(); + clear(); +} + +void ChannelList::clear() +{ + mChannelList->clear(); + channelSearch->clear(); + channelSearch->setFocus(); +} + +void ChannelList::search() +{ + if( m_engine->isConnected() || !channelCache.isEmpty() ) + { + mChannelList->clear(); + mChannelList->setSorting( -1 ); + mSearchButton->setEnabled(false); + mSearch = channelSearch->text(); + mSearching = true; + mUsers = numUsers->value(); + + if( channelCache.isEmpty() ) + m_engine->list(); + else + { + cacheIterator = channelCache.begin(); + slotSearchCache(); + } + } + else + { + KMessageBox::queuedMessageBox( + this, KMessageBox::Error, + i18n("You must be connected to the IRC server to perform a channel listing."), + i18n("Not Connected"), 0 + ); + } +} + +void ChannelList::slotChannelListed( const QString &channel, uint users, const QString &topic ) +{ + checkSearchResult( channel, users, topic ); + channelCache.insert( channel, QPair< uint, QString >( users, topic ) ); +} + +void ChannelList::checkSearchResult( const QString &channel, uint users, const QString &topic ) +{ + if( ( mUsers == 0 || mUsers <= users ) && + ( mSearch.isEmpty() || channel.contains( mSearch, false ) || topic.contains( mSearch, false ) ) + ) + { + new ChannelListItem( mChannelList, channel, QString::number(users), topic ); + } +} + +void ChannelList::slotSearchCache() +{ + if( cacheIterator != channelCache.end() ) + { + checkSearchResult( cacheIterator.key(), cacheIterator.data().first, cacheIterator.data().second ); + ++cacheIterator; + QTimer::singleShot( 0, this, SLOT( slotSearchCache() ) ); + } + else + { + slotListEnd(); + } +} + +void ChannelList::slotListEnd() +{ + mChannelList->setSorting(0, true); + mSearchButton->setEnabled(true); + mSearching = false; +} + +#include "channellist.moc" diff --git a/kopete/protocols/irc/ui/channellist.h b/kopete/protocols/irc/ui/channellist.h new file mode 100644 index 00000000..c6f435a0 --- /dev/null +++ b/kopete/protocols/irc/ui/channellist.h @@ -0,0 +1,80 @@ + /* + channellist.h - IRC Channel Search Widget + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 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. * + * * + ************************************************************************* +*/ + +#ifndef CHANNELLIST_H +#define CHANNELLIST_H + +#include <qwidget.h> +#include <qmap.h> +#include <qpair.h> + +#include "kircengine.h" + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QLabel; +class QLineEdit; +class QPushButton; +class KListView; +class QSpinBox; +class QListViewItem; + +class ChannelList + : public QWidget +{ + Q_OBJECT + + public: + ChannelList( QWidget *parent, KIRC::Engine *engine ); + + public slots: + void search(); + void reset(); + void clear(); + + signals: + void channelDoubleClicked( const QString &channel ); + void channelSelected( const QString &channel ); + + private slots: + void slotItemDoubleClicked( QListViewItem * i ); + void slotItemSelected( QListViewItem * i ); + void slotChannelListed( const QString & channel, uint users, const QString & topic ); + void slotListEnd(); + void slotSearchCache(); + void slotStatusChanged( KIRC::Engine::Status ); + + private: + void checkSearchResult( const QString & channel, uint users, const QString & topic ); + + QLabel* textLabel1_2; + QLineEdit* channelSearch; + QSpinBox* numUsers; + QPushButton* mSearchButton; + KListView* mChannelList; + QVBoxLayout* ChannelListLayout; + QHBoxLayout* layout72_2; + KIRC::Engine *m_engine; + bool mSearching; + QString mSearch; + uint mUsers; + QMap< QString, QPair< uint, QString > > channelCache; + QMap< QString, QPair< uint, QString > >::const_iterator cacheIterator; +}; + +#endif diff --git a/kopete/protocols/irc/ui/channellistdialog.cpp b/kopete/protocols/irc/ui/channellistdialog.cpp new file mode 100644 index 00000000..46128730 --- /dev/null +++ b/kopete/protocols/irc/ui/channellistdialog.cpp @@ -0,0 +1,61 @@ +/* + channellistdialog.cpp - IRC Channel Search Dialog + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + Copyright (c) 2005 by Michel Hermier <michel.hermier@wanadoo.fr> + + Kopete (c) 2002-2005 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 "channellistdialog.h" + +#include "kircengine.h" + +#include "kopeteuiglobal.h" + +#include "qlayout.h" + +ChannelListDialog::ChannelListDialog(KIRC::Engine *engine, const QString &caption, QObject *target, const char* slotJoinChan) + : KDialogBase(Kopete::UI::Global::mainWidget(), "channel_list_widget", false, caption, Close) +{ + m_engine = engine; + m_list = new ChannelList( this, engine ); + + connect( m_list, SIGNAL( channelDoubleClicked( const QString & ) ), + target, slotJoinChan ); + + connect( m_list, SIGNAL( channelDoubleClicked( const QString & ) ), + this, SLOT( slotChannelDoubleClicked( const QString & ) ) ); + + new QHBoxLayout( m_list, 0, spacingHint() ); + + setInitialSize( QSize( 500, 400 ) ); + setMainWidget( m_list ); + show(); +} + +void ChannelListDialog::clear() +{ + m_list->clear(); +} + +void ChannelListDialog::search() +{ + m_list->search(); +} + +void ChannelListDialog::slotChannelDoubleClicked( const QString & ) +{ + close(); +} + +#include "channellistdialog.moc" diff --git a/kopete/protocols/irc/ui/channellistdialog.h b/kopete/protocols/irc/ui/channellistdialog.h new file mode 100644 index 00000000..2bb85f5b --- /dev/null +++ b/kopete/protocols/irc/ui/channellistdialog.h @@ -0,0 +1,45 @@ + /* + channellist.h - IRC Channel Search Widget + + Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org> + + Kopete (c) 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. * + * * + ************************************************************************* +*/ + +#ifndef CHANNELLISTDIALOG_H +#define CHANNELLISTDIALOG_H + +#include "channellist.h" + +#include "kdialogbase.h" + +class ChannelListDialog + : public KDialogBase +{ + Q_OBJECT + + public: + ChannelListDialog(KIRC::Engine *engine, const QString &caption, QObject *target, const char* slotJoinChan); + + void clear(); + + void search(); + + private slots: + void slotChannelDoubleClicked( const QString & ); + + private: + KIRC::Engine *m_engine; + ChannelList *m_list; +}; + +#endif diff --git a/kopete/protocols/irc/ui/empty.cpp b/kopete/protocols/irc/ui/empty.cpp new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/kopete/protocols/irc/ui/empty.cpp @@ -0,0 +1 @@ + diff --git a/kopete/protocols/irc/ui/ircadd.ui b/kopete/protocols/irc/ui/ircadd.ui new file mode 100644 index 00000000..f1025112 --- /dev/null +++ b/kopete/protocols/irc/ui/ircadd.ui @@ -0,0 +1,163 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>ircAddUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>ircAddUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>389</width> + <height>350</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget3</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Add Contact</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>6</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout70</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>N&ickname/channel to add:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>addID</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The name of the IRC contact or channel you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the IRC contact or channel you would like to add. You may type simply the text of a person's nickname, or you may type a channel name, preceded by a pound sign ('#').</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>addID</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The name of the IRC contact or channel you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The name of the IRC contact or channel you would like to add. You may type simply the text of a person's nickname, or you may type a channel name, preceded by a pound sign ('#')</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string><i>(for example: joe_bob or #somechannel)</i></string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer25</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>110</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>&Search Channels</string> + </attribute> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QHBox"> + <property name="name"> + <cstring>hbox</cstring> + </property> + </widget> + </hbox> + </widget> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>QHBox</class> + <header>qhbox.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>5</hordata> + <verdata>5</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XPM.GZ" length="4462">789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495</data> + </image> +</images> +<tabstops> + <tabstop>addID</tabstop> + <tabstop>tabWidget3</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/irc/ui/irceditaccount.ui b/kopete/protocols/irc/ui/irceditaccount.ui new file mode 100644 index 00000000..682e9be9 --- /dev/null +++ b/kopete/protocols/irc/ui/irceditaccount.ui @@ -0,0 +1,1022 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>IRCEditAccountBase</class> +<widget class="QWidget"> + <property name="name"> + <cstring>IRCEditAccountBase</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>689</width> + <height>528</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="baseSize"> + <size> + <width>440</width> + <height>575</height> + </size> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget"> + <property name="name"> + <cstring>tabWidget2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>B&asic Setup</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>150</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>32767</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string><p><b>Note:</b> Most IRC servers do not require a password, and only a nickname is required to connect</p></string> + </property> + <property name="alignment"> + <set>WordBreak|AlignTop</set> + </property> + </widget> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>groupBox59</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>Account Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>N&ickname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mNickName</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the name that everyone will see everytime you say something</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Alternate ni&ckname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mAltNickname</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>When the nickname is already in use when connecting, this name will be used instead</string> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>mNickName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>This is the name that everyone will see everytime you say something</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The alias you would like to use on IRC. You may change this once online with the /nick command.</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>mAltNickname</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>When the nickname is already in use when connecting, this name will be used instead</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When the nickname is already in use when connecting, this name will be used instead</string> + </property> + </widget> + <widget class="Kopete::UI::PasswordWidget" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>mPasswordWidget</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>m_realNameLabel</cstring> + </property> + <property name="text"> + <string>&Real name:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_realNameLineEdit</cstring> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Username:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>mUserName</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>mUserName</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="echoMode"> + <enum>Normal</enum> + </property> + <property name="toolTip" stdset="0"> + <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>m_realNameLineEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="echoMode"> + <enum>Normal</enum> + </property> + <property name="toolTip" stdset="0"> + <string>The username you would prefer to use on IRC, if your system does not have identd support.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Connection</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout21</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>description</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer11</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>161</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox"> + <property name="name"> + <cstring>network</cstring> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>editButton</cstring> + </property> + <property name="text"> + <string>&Edit...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>392</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>&Network:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>network</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Connection Preferences</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>preferSSL</cstring> + </property> + <property name="text"> + <string>&Prefer SSL-based connections</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>autoConnect</cstring> + </property> + <property name="text"> + <string>E&xclude from connect all</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout25</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2_2</cstring> + </property> + <property name="text"> + <string>Default &charset:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>charset</cstring> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>charset</cstring> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer6_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="title"> + <string>Default Messages</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&Part message:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>partMessage</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Quit message:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>quitMessage</cstring> + </property> + </widget> + <widget class="QLineEdit" row="0" column="1"> + <property name="name"> + <cstring>partMessage</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The message you want people to see when you part a channel without giving a reason. Leave this field blank to use the Kopete default message.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The message you want people to see when you part a channel without giving a reason. Leave this field blank to use the Kopete default message.</string> + </property> + </widget> + <widget class="QLineEdit" row="1" column="1"> + <property name="name"> + <cstring>quitMessage</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The message you want people to see when you disconnect from IRC without giving a reason. Leave this field blank to use the Kopete default message.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The message you want people to see when you disconnect from IRC without giving a reason. Leave this field blank to use the Kopete default message.</string> + </property> + </widget> + </grid> + </widget> + <spacer> + <property name="name"> + <cstring>spacer72</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>150</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>A&dvanced Configuration</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox7</cstring> + </property> + <property name="title"> + <string>Message Destinations</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="1" column="1"> + <property name="name"> + <cstring>autoShowAnonWindows</cstring> + </property> + <property name="text"> + <string>Auto-show anonymous windows</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>autoShowServerWindow</cstring> + </property> + <property name="text"> + <string>Auto-show the server window</string> + </property> + </widget> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Server messages:</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel4_3</cstring> + </property> + <property name="text"> + <string>Server notices:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Active Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Server Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Anonymous Window</string> + </property> + </item> + <item> + <property name="text"> + <string>KNotify</string> + </property> + </item> + <item> + <property name="text"> + <string>Ignore</string> + </property> + </item> + <property name="name"> + <cstring>serverNotices</cstring> + </property> + <property name="currentItem"> + <number>1</number> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Active Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Server Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Anonymous Window</string> + </property> + </item> + <item> + <property name="text"> + <string>KNotify</string> + </property> + </item> + <item> + <property name="text"> + <string>Ignore</string> + </property> + </item> + <property name="name"> + <cstring>serverMessages</cstring> + </property> + <property name="currentItem"> + <number>1</number> + </property> + </widget> + </grid> + </widget> + <widget class="QLayoutWidget" row="0" column="1"> + <property name="name"> + <cstring>layout23</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel3_3</cstring> + </property> + <property name="text"> + <string>Error messages:</string> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>Active Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Server Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Anonymous Window</string> + </property> + </item> + <item> + <property name="text"> + <string>KNotify</string> + </property> + </item> + <item> + <property name="text"> + <string>Ignore</string> + </property> + </item> + <property name="name"> + <cstring>informationReplies</cstring> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Information replies:</string> + </property> + </widget> + <widget class="QComboBox" row="1" column="1"> + <item> + <property name="text"> + <string>Active Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Server Window</string> + </property> + </item> + <item> + <property name="text"> + <string>Anonymous Window</string> + </property> + </item> + <item> + <property name="text"> + <string>KNotify</string> + </property> + </item> + <item> + <property name="text"> + <string>Ignore</string> + </property> + </item> + <property name="name"> + <cstring>errorMessages</cstring> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>130</height> + </size> + </property> + <property name="title"> + <string>Custom CTCP Replies</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KListView"> + <column> + <property name="text"> + <string>CTCP</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Reply</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>ctcpList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>2</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="allColumnsShowFocus"> + <bool>false</bool> + </property> + <property name="defaultRenameAction"> + <enum>Accept</enum> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + <property name="itemsRenameable"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>You can use this dialog to add custom replies for when people send CTCP requests to you. You can also use this dialog to override the built-in replies for VERSION, USERINFO, and CLIENTINFO.</string> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout153</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel3_2</cstring> + </property> + <property name="text"> + <string>&CTCP:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>newCTCP</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>newCTCP</cstring> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel4_2</cstring> + </property> + <property name="text"> + <string>&Reply:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>newReply</cstring> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>newReply</cstring> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>addReply</cstring> + </property> + <property name="text"> + <string>Add Repl&y</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox60</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>130</height> + </size> + </property> + <property name="title"> + <string>Run Following Commands on Connect</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget" row="1" column="0"> + <property name="name"> + <cstring>layout29</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit"> + <property name="name"> + <cstring>commandEdit</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>addButton</cstring> + </property> + <property name="text"> + <string>Add Co&mmand</string> + </property> + </widget> + </hbox> + </widget> + <widget class="KListView" row="0" column="0"> + <column> + <property name="text"> + <string>Command</string> + </property> + <property name="clickable"> + <bool>true</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>commandList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>2</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="defaultRenameAction"> + <enum>Accept</enum> + </property> + <property name="fullWidth"> + <bool>true</bool> + </property> + <property name="itemsRenameable"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Any commands added here will be run as soon as you are connected to the IRC server.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Any commands added here will be run as soon as you are connected to the IRC server.</string> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + </widget> + </hbox> +</widget> +<customwidgets> + <customwidget> + <class>Kopete::UI::PasswordWidget</class> + <header location="local">kopetepasswordwidget.h</header> + <sizehint> + <width>50</width> + <height>50</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>changed()</signal> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="826">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000030149444154388db59531681b5718c77f0e377c070e3c810a3a70e0041eac51852e0a19e45134830a1d9a4c69a04bc8928e990a693a640e1d0c8642b08742321894c1507991b484c890902bb8701a047760c3bd21701fe4201dde49b6a41a32b8df72dcbbeffdbefffbbfefbd5b1b0c07cce266ebe667ae2006c3c1dada0cdc3be87d6e6c35b0d692a409d9c7ec8b20d65ae29398d19b1114e7e3de4ce98b3f5e10dc0053cf0951b4506496e1b964bf7ce6c585d9054c62d01d617ca48be0596553cf496d8f2c8b01c5f795fc93904e85ec4c01a152857a5d9175d0b2805c872080f18595ccc1499a10a225d4e2fbc2877786fe81253ab6c04c8d106e09db5d43ab0d146e5c64d1a23938fb98a185cea1c33eecfd9eba49eb427dcb201e245365f2b7b2fb5b4a3a31dcb927178afe07d86901df870fefa4842aed6f6b74ba42e52b4014d580e1eb9cbd9d94de7e4aad16d2f9be02d805f0b5e532f927a1ffcacea1777f122a8105b164a7c25faf323a5d9f1f1fd600e1e5bec59e2d4b5c7ef5209d0ad17b8b31864e57c0b3e0815ac3ee33253ab664a770ff5185d1a1cb8d2267d3e58aa1dc7d2508cbe597d0e74fdd269aaaf0f52d414c4ea3e9762c996869e42560d7a72e41c4799a2586e74f95e8d8151481fa86efbe7b3398ac58b1a2b8527589f15451ad303ac2293542ad6648a796278f13a27185e4c4754310facb98c53a79e19a3fdc1426ff28c3d7399d1f7cb25343eb96106cf83c790ce9c4f2eb831855c55485663327992eb6dc8a6259874ed700b0b793323cccb9ffa842b30d6133e3e75fea989ac15a8b16ca76b746b0b92278d919774c5b6d48a78697fb29bbcf52468742a32120909c24e899ce67beed5be2db01e22d1e9485bb620e47f9ee9e606a21bd3f5d3744c7e7c54d55e87443867d8b554515ac5db4620e8e4f62263170fd1cdee90aad7640141992891b0f367c9adfe4049bb07d3b7022bd8c687c0978f46684ee084150b65ac1fcca94591b7a90a496e4c095164fb016a2b192a497795cc0f84817aebe25f7bf70ccc54a575c555c03f78ffa5fc0570d1f0c076bff0232285a09643cc7ce0000000049454e44ae426082</data> + </image> +</images> +<tabstops> + <tabstop>tabWidget2</tabstop> + <tabstop>mNickName</tabstop> + <tabstop>mAltNickname</tabstop> + <tabstop>mUserName</tabstop> + <tabstop>m_realNameLineEdit</tabstop> + <tabstop>network</tabstop> + <tabstop>editButton</tabstop> + <tabstop>preferSSL</tabstop> + <tabstop>autoConnect</tabstop> + <tabstop>charset</tabstop> + <tabstop>partMessage</tabstop> + <tabstop>quitMessage</tabstop> + <tabstop>serverMessages</tabstop> + <tabstop>serverNotices</tabstop> + <tabstop>informationReplies</tabstop> + <tabstop>errorMessages</tabstop> + <tabstop>autoShowServerWindow</tabstop> + <tabstop>autoShowAnonWindows</tabstop> + <tabstop>ctcpList</tabstop> + <tabstop>newCTCP</tabstop> + <tabstop>newReply</tabstop> + <tabstop>addReply</tabstop> + <tabstop>commandList</tabstop> + <tabstop>commandEdit</tabstop> + <tabstop>addButton</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>klistview.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/irc/ui/irceditaccountwidget.cpp b/kopete/protocols/irc/ui/irceditaccountwidget.cpp new file mode 100644 index 00000000..4a1e6ed3 --- /dev/null +++ b/kopete/protocols/irc/ui/irceditaccountwidget.cpp @@ -0,0 +1,282 @@ +/* + irceditaccountwidget.cpp - IRC Account Widget + + Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi> + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org> + Kopete (c) 2003-2005 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 "irceditaccountwidget.h" + +#include "ircaccount.h" +#include "ircusercontact.h" +#include "ircprotocol.h" +#include "kcodecaction.h" + +#include "kircengine.h" + +#include "kopetepasswordwidget.h" + +#include <kmessagebox.h> +#include <klocale.h> +#include <klistview.h> +#include <kdebug.h> +#include <kextsock.h> +#include <kconfig.h> +#include <kglobal.h> +#include <kcharsets.h> + +#include <qlabel.h> +#include <qpopupmenu.h> +#include <qpushbutton.h> +#include <qcheckbox.h> +#include <qconnection.h> +#include <qvalidator.h> +#include <qcombobox.h> +#include <qlistbox.h> +#include <qlineedit.h> + +IRCEditAccountWidget::IRCEditAccountWidget(IRCProtocol *proto, IRCAccount *ident, QWidget *parent, const char * ) + : IRCEditAccountBase(parent), KopeteEditAccountWidget(ident) +{ + mProtocol = proto; + + // default charset/encoding for new accounts: utf-8, see http://www.iana.org/assignments/character-sets + int currentCodec = 106; + + if( account() ) + { + QString nickName = account()->mySelf()->nickName(); + QString serverInfo = account()->accountId(); + + mNickName->setText( nickName ); + mAltNickname->setText( account()->altNick() ); + mUserName->setText( account()->userName() ); + m_realNameLineEdit->setText( account()->realName() ); + + partMessage->setText( account()->defaultPart() ); + quitMessage->setText( account()->defaultQuit() ); + if( account()->codec() ) + currentCodec = account()->codec()->mibEnum(); + + mPasswordWidget->load ( &account()->password() ); + + preferSSL->setChecked(account()->configGroup()->readBoolEntry("PreferSSL")); + autoShowServerWindow->setChecked( account()->configGroup()->readBoolEntry("AutoShowServerWindow") ); + autoConnect->setChecked( static_cast<Kopete::Account*>(account())->excludeConnect() ); + + KConfigGroup *config = account()->configGroup(); + + serverNotices->setCurrentItem( config->readNumEntry( "ServerNotices", IRCAccount::ServerWindow ) - 1 ); + serverMessages->setCurrentItem( config->readNumEntry( "ServerMessages", IRCAccount::ServerWindow ) - 1 ); + informationReplies->setCurrentItem( config->readNumEntry( "InformationReplies", IRCAccount::ActiveWindow ) - 1 ); + errorMessages->setCurrentItem( config->readNumEntry( "ErrorMessages", IRCAccount::ActiveWindow ) - 1 ); + + QStringList cmds = account()->connectCommands(); + for( QStringList::Iterator i = cmds.begin(); i != cmds.end(); ++i ) + new QListViewItem( commandList, *i ); + + const QMap< QString, QString > replies = account()->customCtcpReplies(); + for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it ) + new QListViewItem( ctcpList, it.key(), it.data() ); + } + + mUserName->setValidator( new QRegExpValidator( QString::fromLatin1("^[^\\s]*$"), mUserName ) ); + mNickName->setValidator( new QRegExpValidator( QString::fromLatin1("^[^#+&][^\\s]*$"), mNickName ) ); + mAltNickname->setValidator( new QRegExpValidator( QString::fromLatin1("^[^#+&][^\\s]*$"), mAltNickname ) ); + + charset->insertStringList( KCodecAction::supportedEncodings() ); + + for (int i = 0; i < charset->count(); ++i) { + QString encoding = KGlobal::charsets()->encodingForName(charset->text(i)); + + if (KGlobal::charsets()->codecForName(encoding)->mibEnum() == currentCodec) { + charset->setCurrentItem( i ); + break; + } + } + + connect( commandList, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ), + this, SLOT( slotCommandContextMenu( KListView *, QListViewItem *, const QPoint & ) ) ); + + connect( ctcpList, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ), + this, SLOT( slotCtcpContextMenu( KListView *, QListViewItem *, const QPoint & ) ) ); + + connect( addButton, SIGNAL( clicked() ), this, SLOT( slotAddCommand() ) ); + connect( editButton, SIGNAL( clicked() ), this, SLOT(slotEditNetworks() ) ); + connect( addReply, SIGNAL( clicked() ), this, SLOT( slotAddCtcp() ) ); + + connect( network, SIGNAL( activated( const QString & ) ), + this, SLOT( slotUpdateNetworkDescription( const QString &) ) ); + + connect( IRCProtocol::protocol(), SIGNAL( networkConfigUpdated( const QString & ) ), + this, SLOT( slotUpdateNetworks( const QString & ) ) ); + + slotUpdateNetworks( QString::null ); +} + +IRCEditAccountWidget::~IRCEditAccountWidget() +{ +} + +IRCAccount *IRCEditAccountWidget::account () +{ + return dynamic_cast<IRCAccount *>(KopeteEditAccountWidget::account () ); +} + +void IRCEditAccountWidget::slotUpdateNetworks( const QString & selectedNetwork ) +{ + network->clear(); + + uint i = 0; + QStringList keys; + for( QDictIterator<IRCNetwork> it( IRCProtocol::protocol()->networks() ); it.current(); ++it ) + keys.append( it.currentKey() ); + + keys.sort(); + + QStringList::Iterator end = keys.end(); + for( QStringList::Iterator it = keys.begin(); it != end; ++it ) + { + IRCNetwork * current = IRCProtocol::protocol()->networks()[*it]; + network->insertItem( current->name ); + if ( ( account() && account()->networkName() == current->name ) || current->name == selectedNetwork ) + { + network->setCurrentItem( i ); + description->setText( current->description ); + } + ++i; + } +} + +void IRCEditAccountWidget::slotEditNetworks() +{ + IRCProtocol::protocol()->editNetworks( network->currentText() ); +} + +void IRCEditAccountWidget::slotUpdateNetworkDescription( const QString &network ) +{ + description->setText( + IRCProtocol::protocol()->networks()[ network ]->description + ); +} + +void IRCEditAccountWidget::slotCommandContextMenu( KListView *, QListViewItem *item, const QPoint &p ) +{ + QPopupMenu popup; + popup.insertItem( i18n("Remove Command"), 1 ); + if( popup.exec( p ) == 1 ) + delete item; +} + +void IRCEditAccountWidget::slotCtcpContextMenu( KListView *, QListViewItem *item, const QPoint &p ) +{ + QPopupMenu popup; + popup.insertItem( i18n("Remove CTCP Reply"), 1 ); + if( popup.exec( p ) == 1 ) + delete item; +} + +void IRCEditAccountWidget::slotAddCommand() +{ + if ( !commandEdit->text().isEmpty() ) + { + new QListViewItem( commandList, commandEdit->text() ); + commandEdit->clear(); + } +} + +void IRCEditAccountWidget::slotAddCtcp() +{ + if ( !newCTCP->text().isEmpty() && !newReply->text().isEmpty() ) + { + new QListViewItem( ctcpList, newCTCP->text(), newReply->text() ); + newCTCP->clear(); + newReply->clear(); + } +} + +QString IRCEditAccountWidget::generateAccountId( const QString &network ) +{ + KConfig *config = KGlobal::config(); + QString nextId = network; + + uint accountNumber = 1; + while( config->hasGroup( QString("Account_%1_%2").arg( m_protocol->pluginId() ).arg( nextId ) ) ) + { + nextId = QString::fromLatin1("%1_%2").arg( network ).arg( ++accountNumber ); + } + kdDebug( 14120 ) << k_funcinfo << " ID IS: " << nextId << endl; + return nextId; +} + +Kopete::Account *IRCEditAccountWidget::apply() +{ + QString nickName = mNickName->text(); + QString networkName = network->currentText(); + + if( !account() ) + { + setAccount( new IRCAccount( mProtocol, generateAccountId(networkName), QString::null, networkName, nickName ) ); + + } + else + { + account()->setNickName( nickName ); + account()->setNetwork( networkName ); + } + + mPasswordWidget->save( &account()->password() ); + + account()->setAltNick( mAltNickname->text() ); + account()->setUserName( mUserName->text() ); + account()->setRealName( m_realNameLineEdit->text() ); + account()->setDefaultPart( partMessage->text() ); + account()->setDefaultQuit( quitMessage->text() ); + account()->setAutoShowServerWindow( autoShowServerWindow->isChecked() ); + account()->setExcludeConnect( autoConnect->isChecked() ); + account()->setMessageDestinations( serverNotices->currentItem() + 1, serverMessages->currentItem() + 1, + informationReplies->currentItem() + 1, errorMessages->currentItem() + 1 + ); + + account()->configGroup()->writeEntry("PreferSSL", preferSSL->isChecked()); + + QStringList cmds; + for( QListViewItem *i = commandList->firstChild(); i; i = i->nextSibling() ) + cmds.append( i->text(0) ); + + QMap< QString, QString > replies; + for( QListViewItem *i = ctcpList->firstChild(); i; i = i->nextSibling() ) + replies[ i->text(0) ] = i->text(1); + + account()->setCustomCtcpReplies( replies ); + account()->setConnectCommands( cmds ); + + KCharsets *c = KGlobal::charsets(); + account()->setCodec( c->codecForName( c->encodingForName( charset->currentText() ) ) ); + + return account(); +} + + +bool IRCEditAccountWidget::validateData() +{ + if( mNickName->text().isEmpty() ) + KMessageBox::sorry(this, i18n("<qt>You must enter a nickname.</qt>"), i18n("Kopete")); + else + return true; + + return false; +} + +#include "irceditaccountwidget.moc" diff --git a/kopete/protocols/irc/ui/irceditaccountwidget.h b/kopete/protocols/irc/ui/irceditaccountwidget.h new file mode 100644 index 00000000..365acaf3 --- /dev/null +++ b/kopete/protocols/irc/ui/irceditaccountwidget.h @@ -0,0 +1,60 @@ +/* + irceditaccountwidget.h - IRC Account Widget + + Kopete (c) 2003 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. * + * * + ************************************************************************* +*/ + + + +#ifndef IRCEDITACCOUNTWIDEGET_H +#define IRCEDITACCOUNTWIDEGET_H + +#include "editaccountwidget.h" +#include "irceditaccount.h" + +class IRCProtocol; +class IRCAccount; +class KListView; +class QListViewItem; + +class IRCEditAccountWidget : public IRCEditAccountBase, public KopeteEditAccountWidget +{ + Q_OBJECT + + public: + IRCEditAccountWidget(IRCProtocol *proto, IRCAccount *, QWidget *parent=0, const char *name=0); + ~IRCEditAccountWidget(); + + IRCAccount *account(); + virtual bool validateData(); + virtual Kopete::Account *apply(); + + private slots: + void slotCommandContextMenu( KListView*, QListViewItem*, const QPoint & ); + void slotCtcpContextMenu( KListView*, QListViewItem*, const QPoint & ); + void slotAddCommand(); + void slotAddCtcp(); + void slotEditNetworks(); + void slotUpdateNetworks( const QString & ); + void slotUpdateNetworkDescription( const QString & ); + + private: + void readNetworks(); + QString generateAccountId( const QString &network ); + + IRCProtocol *mProtocol; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/irc/ui/networkconfig.ui b/kopete/protocols/irc/ui/networkconfig.ui new file mode 100644 index 00000000..d1000e37 --- /dev/null +++ b/kopete/protocols/irc/ui/networkconfig.ui @@ -0,0 +1,382 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NetworkConfig</class> +<widget class="QDialog"> + <property name="name"> + <cstring>NetworkConfig</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>670</width> + <height>468</height> + </rect> + </property> + <property name="caption"> + <string>Network Configuration</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLineEdit" row="1" column="4" rowspan="1" colspan="3"> + <property name="name"> + <cstring>description</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>textLabel10</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Description:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>description</cstring> + </property> + </widget> + <widget class="QGroupBox" row="2" column="3" rowspan="1" colspan="4"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="margin"> + <number>4</number> + </property> + <property name="title"> + <string>Host Con&figuration</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QListBox" row="0" column="0" rowspan="3" colspan="4"> + <property name="name"> + <cstring>hostList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>The IRC servers associated with this network</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The IRC servers associated with this network. Use the up and down buttons to alter the order in which connections are attempted.</string> + </property> + </widget> + <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="4"> + <property name="name"> + <cstring>password</cstring> + </property> + <property name="echoMode"> + <enum>Password</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Most IRC servers do not require a password</string> + </property> + </widget> + <widget class="QLabel" row="3" column="2"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="text"> + <string>Por&t:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>port</cstring> + </property> + </widget> + <widget class="QSpinBox" row="3" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>port</cstring> + </property> + <property name="maxValue"> + <number>65536</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>6667</number> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>&Password:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>password</cstring> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>&Host:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>host</cstring> + </property> + </widget> + <widget class="QLineEdit" row="3" column="1"> + <property name="name"> + <cstring>host</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string></string> + </property> + </widget> + <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>useSSL</cstring> + </property> + <property name="text"> + <string>Use SS&L</string> + </property> + <property name="toolTip" stdset="0"> + <string>Check this to enable SSL for this connection</string> + </property> + </widget> + <widget class="QPushButton" row="6" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>removeHost</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Remove</string> + </property> + </widget> + <widget class="QPushButton" row="6" column="2"> + <property name="name"> + <cstring>newHost</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&New...</string> + </property> + </widget> + <spacer row="6" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>210</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="2" column="4"> + <property name="name"> + <cstring>downButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Down</string> + </property> + <property name="toolTip" stdset="0"> + <string>Move this server down</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Move this server down in connection attempt priority</string> + </property> + </widget> + <spacer row="0" column="4"> + <property name="name"> + <cstring>spacer4</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>151</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="1" column="4"> + <property name="name"> + <cstring>upButton</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Up</string> + </property> + <property name="toolTip" stdset="0"> + <string>Move this server up</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Move this server up in connection attempt priority</string> + </property> + </widget> + </grid> + </widget> + <widget class="QPushButton" row="3" column="6"> + <property name="name"> + <cstring>cancelButton</cstring> + </property> + <property name="text"> + <string>&Cancel</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="5"> + <property name="name"> + <cstring>saveButton</cstring> + </property> + <property name="text"> + <string>&Save</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="0"> + <property name="name"> + <cstring>newNetwork</cstring> + </property> + <property name="text"> + <string>Ne&w</string> + </property> + </widget> + <widget class="QListBox" row="0" column="0" rowspan="3" colspan="3"> + <property name="name"> + <cstring>networkList</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <spacer row="3" column="3" rowspan="1" colspan="2"> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>260</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>renameNetwork</cstring> + </property> + <property name="text"> + <string>Rena&me...</string> + </property> + </widget> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>removeNetwork</cstring> + </property> + <property name="text"> + <string>Remo&ve</string> + </property> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>reject()</slot> + </connection> + <connection> + <sender>saveButton</sender> + <signal>clicked()</signal> + <receiver>NetworkConfig</receiver> + <slot>accept()</slot> + </connection> +</connections> +<tabstops> + <tabstop>networkList</tabstop> + <tabstop>newNetwork</tabstop> + <tabstop>renameNetwork</tabstop> + <tabstop>removeNetwork</tabstop> + <tabstop>description</tabstop> + <tabstop>hostList</tabstop> + <tabstop>upButton</tabstop> + <tabstop>downButton</tabstop> + <tabstop>host</tabstop> + <tabstop>port</tabstop> + <tabstop>password</tabstop> + <tabstop>useSSL</tabstop> + <tabstop>newHost</tabstop> + <tabstop>removeHost</tabstop> + <tabstop>saveButton</tabstop> + <tabstop>cancelButton</tabstop> +</tabstops> +<signals> + <signal>accepted()</signal> + <signal>rejected()</signal> +</signals> +<slots> + <slot access="protected">accept()</slot> + <slot access="protected">reject()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/irc/ui/networkconfig.ui.h b/kopete/protocols/irc/ui/networkconfig.ui.h new file mode 100644 index 00000000..7716e75f --- /dev/null +++ b/kopete/protocols/irc/ui/networkconfig.ui.h @@ -0,0 +1,26 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + + + + + + +void NetworkConfig::accept() +{ + emit accepted(); + QDialog::accept(); +} + + +void NetworkConfig::reject() +{ + emit rejected(); + QDialog::reject(); +} |