summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/irc/libkirc
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/irc/libkirc')
-rw-r--r--kopete/protocols/irc/libkirc/Makefile.am20
-rw-r--r--kopete/protocols/irc/libkirc/kircengine.cpp497
-rw-r--r--kopete/protocols/irc/libkirc/kircengine.h532
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_commands.cpp312
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_ctcp.cpp351
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp570
-rw-r--r--kopete/protocols/irc/libkirc/kircentity.cpp132
-rw-r--r--kopete/protocols/irc/libkirc/kircentity.h128
-rw-r--r--kopete/protocols/irc/libkirc/kircmessage.cpp370
-rw-r--r--kopete/protocols/irc/libkirc/kircmessage.h198
-rw-r--r--kopete/protocols/irc/libkirc/kircmessageredirector.cpp97
-rw-r--r--kopete/protocols/irc/libkirc/kircmessageredirector.h86
-rw-r--r--kopete/protocols/irc/libkirc/kirctransfer.cpp365
-rw-r--r--kopete/protocols/irc/libkirc/kirctransfer.h191
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferhandler.cpp97
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferhandler.h79
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferserver.cpp154
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferserver.h81
-rw-r--r--kopete/protocols/irc/libkirc/ksslsocket.cpp458
-rw-r--r--kopete/protocols/irc/libkirc/ksslsocket.h68
20 files changed, 4786 insertions, 0 deletions
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 <[email protected]>
+ Copyright (c) 2003-2004 by Michel Hermier <[email protected]>
+ Copyright (c) 2002 by Nick Betcher <[email protected]>
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+ Copyright (c) 2003 by Jason Keirstead <[email protected]>
+ Copyright (c) 2002 by Nick Betcher <[email protected]>
+
+ Kopete (c) 2002-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+ * @author Michel Hermier <[email protected]>
+ * @author Jason Keirstead <[email protected]>
+ */
+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 &currentHost() 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 &params);
+ 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 <[email protected]>
+ Copyright (c) 2002 by Nick Betcher <[email protected]>
+ Copyright (c) 2003 by Jason Keirstead <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+ Copyright (c) 2002 by Nick Betcher <[email protected]>
+ Copyright (c) 2003 by Jason Keirstead <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+ Copyright (c) 2002 by Nick Betcher <[email protected]>
+ Copyright (c) 2003 by Jason Keirstead <[email protected]>
+
+ Kopete (c) 2002-2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003 by the Kopete engineelopers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 &regexp, 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 <[email protected]>
+
+ Kopete (c) 2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 &regexp, 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 <[email protected]>
+
+ Kopete (c) 2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+
+ Kopete (c) 2003-2004 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]>
+ Copyright (c) 2004 by Jason Keirstead <[email protected]>
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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 <[email protected]
+ * Copyright (C) 2001-2003 George Staikos <[email protected]>
+ * Copyright (C) 2001 Dawit Alemayehu <[email protected]>
+*/
+
+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 [email protected]."
+ << 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 <[email protected]>
+ Copyright (c) 2004 by Jason Keirstead <[email protected]>
+
+ Kopete (c) 2002-2005 by the Kopete developers <[email protected]>
+
+ *************************************************************************
+ * *
+ * 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