summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/irc/libkirc/kircengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/irc/libkirc/kircengine.cpp')
-rw-r--r--kopete/protocols/irc/libkirc/kircengine.cpp497
1 files changed, 497 insertions, 0 deletions
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: