From bcb704366cb5e333a626c18c308c7e0448a8e69f Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kopete/protocols/irc/libkirc/Makefile.am | 20 + kopete/protocols/irc/libkirc/kircengine.cpp | 497 ++++++++++++++++++ kopete/protocols/irc/libkirc/kircengine.h | 532 +++++++++++++++++++ .../protocols/irc/libkirc/kircengine_commands.cpp | 312 +++++++++++ kopete/protocols/irc/libkirc/kircengine_ctcp.cpp | 351 +++++++++++++ .../irc/libkirc/kircengine_numericreplies.cpp | 570 +++++++++++++++++++++ kopete/protocols/irc/libkirc/kircentity.cpp | 132 +++++ kopete/protocols/irc/libkirc/kircentity.h | 128 +++++ kopete/protocols/irc/libkirc/kircmessage.cpp | 370 +++++++++++++ kopete/protocols/irc/libkirc/kircmessage.h | 198 +++++++ .../irc/libkirc/kircmessageredirector.cpp | 97 ++++ .../protocols/irc/libkirc/kircmessageredirector.h | 86 ++++ kopete/protocols/irc/libkirc/kirctransfer.cpp | 365 +++++++++++++ kopete/protocols/irc/libkirc/kirctransfer.h | 191 +++++++ .../protocols/irc/libkirc/kirctransferhandler.cpp | 97 ++++ kopete/protocols/irc/libkirc/kirctransferhandler.h | 79 +++ .../protocols/irc/libkirc/kirctransferserver.cpp | 154 ++++++ kopete/protocols/irc/libkirc/kirctransferserver.h | 81 +++ kopete/protocols/irc/libkirc/ksslsocket.cpp | 458 +++++++++++++++++ kopete/protocols/irc/libkirc/ksslsocket.h | 68 +++ 20 files changed, 4786 insertions(+) create mode 100644 kopete/protocols/irc/libkirc/Makefile.am create mode 100644 kopete/protocols/irc/libkirc/kircengine.cpp create mode 100644 kopete/protocols/irc/libkirc/kircengine.h create mode 100644 kopete/protocols/irc/libkirc/kircengine_commands.cpp create mode 100644 kopete/protocols/irc/libkirc/kircengine_ctcp.cpp create mode 100644 kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp create mode 100644 kopete/protocols/irc/libkirc/kircentity.cpp create mode 100644 kopete/protocols/irc/libkirc/kircentity.h create mode 100644 kopete/protocols/irc/libkirc/kircmessage.cpp create mode 100644 kopete/protocols/irc/libkirc/kircmessage.h create mode 100644 kopete/protocols/irc/libkirc/kircmessageredirector.cpp create mode 100644 kopete/protocols/irc/libkirc/kircmessageredirector.h create mode 100644 kopete/protocols/irc/libkirc/kirctransfer.cpp create mode 100644 kopete/protocols/irc/libkirc/kirctransfer.h create mode 100644 kopete/protocols/irc/libkirc/kirctransferhandler.cpp create mode 100644 kopete/protocols/irc/libkirc/kirctransferhandler.h create mode 100644 kopete/protocols/irc/libkirc/kirctransferserver.cpp create mode 100644 kopete/protocols/irc/libkirc/kirctransferserver.h create mode 100644 kopete/protocols/irc/libkirc/ksslsocket.cpp create mode 100644 kopete/protocols/irc/libkirc/ksslsocket.h (limited to 'kopete/protocols/irc/libkirc') 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 + Copyright (c) 2003-2004 by Michel Hermier + Copyright (c) 2002 by Nick Betcher + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 +#endif + +#include "kircengine.h" +#include "ksslsocket.h" + +#include +#include +#include +#include +#include + +#include +#include + +//Needed for getuid / getpwuid +#include +#include +#include + +#include + +#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 &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( 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 &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 + Copyright (c) 2003 by Jason Keirstead + Copyright (c) 2002 by Nick Betcher + + Kopete (c) 2002-2004 by the Kopete developers + + ************************************************************************* + * * + * 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 + +// FIXME: Move the following kdedebug class to the *.cpp. +#include +#if KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 ) +#include +#endif + +#include +#include +#include +#include +#include +#include + +class QRegExp; + +namespace KIRC +{ + +/** + * @author Nick Betcher + * @author Michel Hermier + * @author Jason Keirstead + */ +class Engine + : public QObject +{ + Q_OBJECT + +// Q_PROPERTY(QUrl serverURL READ serverURL WRITE setServerURL) + +// Extracted from the base of the serverURL. +// Q_PROPERTY(bool useSSL); +// Q_PROPERTY(QString user READ user); +// Q_PROPERTY(QString password); +// Q_PROPERTY(QString host READ host); +// Q_PROPERTY(int port READ host); + +// Extracted from the query of the serverURL. +// Q_PROPERTY(bool reqsPasswd); +// Q_PROPERTY(QString name); // real name +// Q_PROPERTY(QStringList nickList READ nickList WRITE setNickList) +// Q_PROPERTY(QString nick READ nick) +// Q_PROPERTY(QStringList portList) + + Q_ENUMS(Status) + +public: + enum Error + { + ParsingFailed, + UnknownCommand, + UnknownNumericReply, + InvalidNumberOfArguments, + MethodFailed + }; + + enum Status + { + Idle, + Connecting, + Authentifying, + Connected, + Closing, + AuthentifyingFailed, + Timeout, + Disconnected + }; + + enum ServerMessageType + { + ErrorMessage = -1, + PrivateMessage, + InfoMessage, + + MessageOfTheDayMessage, + MessageOfTheDayCondensedMessage + }; + + Engine( QObject *parent = 0, const char* name = 0 ); + ~Engine(); + +// QString nick() const; +// QStringList nickList() const; +// void setNickList(const QStringList& nickList); + +// QUrl serverURL() const; +// bool setServerURL(const QUrl &url); + + inline const QString ¤tHost() const + { return m_Host; }; + + inline Q_UINT16 currentPort() + { return m_Port; } + + inline const QString &nickName() const + { return m_Nickname; }; + + inline const QString &password() const + { return m_Passwd; } + + inline void setPassword(const QString &passwd) + { m_Passwd = passwd; }; + + inline const QString &userName() const + { return m_Username; } + + void setUserName(const QString &newName); + + void setRealName(const QString &newName); + inline const QString &realName() const + { return m_realName; } + + inline const bool reqsPassword() const + { return m_ReqsPasswd; }; + + inline void setReqsPassword(bool b) + { m_ReqsPasswd = b; }; + + const bool useSSL() const { return m_useSSL; }; + void setUseSSL( bool useSSL ); + + inline const QTextCodec *codec() const + { return defaultCodec; }; + + const QTextCodec *codecForNick( const QString &nick ) const; + + inline void setDefaultCodec( QTextCodec* codec ) + { defaultCodec = codec; }; + + void setVersionString(const QString &versionString); + void setUserString(const QString &userString); + void setSourceString(const QString &sourceString); + void connectToServer(const QString &host, Q_UINT16 port, const QString &nickname, bool useSSL = false); + + KExtendedSocket *socket() + { return m_sock; }; + + inline KIRC::Engine::Status status() const + { return m_status; } + + inline bool isDisconnected() const + { return m_status == Disconnected || m_status == Idle; } + + inline bool isConnected() const + { return m_status == Connected; } + + inline void setCodec( const QString &nick, const QTextCodec *codec ) + { codecs.replace( nick, codec ); } + + /* Custom CTCP replies handling */ + inline QString &customCtcp( const QString &s ) + { return customCtcpMap[s]; } + + inline void addCustomCtcp( const QString &ctcp, const QString &reply ) + { customCtcpMap[ ctcp.lower() ] = reply; } + + KIRC::EntityPtr getEntity(const QString &name); + +public slots: + //Message output + void writeRawMessage(const QString &message); + + void writeMessage(const QString &message, const QTextCodec *codec = 0 ); + void writeMessage(const QString &command, const QStringList &args, + const QString &suffix = QString::null, const QTextCodec *codec = 0); + + void writeCtcpMessage(const QString &command, const QString &to, const QString &ctcpMessage); + + void writeCtcpMessage(const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true); + + inline void writeCtcpQueryMessage(const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true) + { return writeCtcpMessage("PRIVMSG", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); } + + inline void writeCtcpReplyMessage(const QString &to, const QString &ctcpMessage) + { writeCtcpMessage("NOTICE", to, ctcpMessage); } + + inline void writeCtcpReplyMessage(const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null, + bool emitRepliedCtcp = true) + { return writeCtcpMessage("NOTICE", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); } + + inline void writeCtcpErrorMessage(const QString &to, const QString &ctcpLine, const QString &errorMsg, + bool emitRepliedCtcp=true) + { return writeCtcpReplyMessage(to, QString::null, "ERRMSG", ctcpLine, errorMsg, emitRepliedCtcp); } + + bool bind(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bind(int id, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bindCtcpQuery(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + bool bindCtcpReply(const QString &command, QObject *object, const char *member, + int minArgs = KIRC::MessageRedirector::Unknown, + int maxArgs = KIRC::MessageRedirector::Unknown, + const QString &helpMessage = QString::null); + + + void away(bool isAway, const QString &awayMessage = QString::null); + void ison(const QStringList &nickList); + void join(const QString &name, const QString &key); + void kick(const QString &user, const QString &channel, const QString &reason); + void list(); + void mode(const QString &target, const QString &mode); + void motd(const QString &server = QString::null); + void nick(const QString &newNickname); + void notice(const QString &target, const QString &message); + void part(const QString &name, const QString &reason); + void pass(const QString &password); + void privmsg(const QString &contact, const QString &message); + + /** + * Send a quit message for the given reason. + * If now is set to true the connection is closed and no event message is sent. + * Therefore setting now to true should only be used while destroying the object. + */ + void quit(const QString &reason, bool now=false); + + void topic(const QString &channel, const QString &topic); + void user(const QString &newUsername, const QString &hostname, const QString &newRealname); + void user(const QString &newUsername, Q_UINT8 mode, const QString &newRealname); + void whois(const QString &user); + + + /* CTCP commands */ + void CtcpRequestCommand(const QString &contact, const QString &command); + void CtcpRequest_action(const QString &contact, const QString &message); + void CtcpRequest_dcc(const QString &, const QString &, unsigned int port, KIRC::Transfer::Type type); + void CtcpRequest_ping(const QString &target); + void CtcpRequest_version(const QString &target); + +public slots: + void showInfoDialog(); + +signals: + void statusChanged(KIRC::Engine::Status newStatus); + void internalError(KIRC::Engine::Error, KIRC::Message &); + + void receivedMessage(KIRC::Message &); + + /** + * Emit a received message. + * The received message could have been translated to your locale. + * + * @param type the message type. + * @param from the originator of the message. + * @param to is the list of entities that are related to this message. + * @param msg the message (usually translated). + * + * @note Most of the following numeric messages should be deprecated, and call this method instead. + * Most of the methods, using it, update KIRC::Entities. + * Lists based messages are sent via dedicated API, therefore they don't use this. + */ + // @param args the args to apply to this message. + void receivedMessage( KIRC::Engine::ServerMessageType type, + const KIRC::EntityPtr &from, + const KIRC::EntityPtrList &to, + const QString &msg); + + void successfullyChangedNick(const QString &, const QString &); + + //ServerContact Signals + void incomingMotd(const QString &motd); + void incomingNotice(const QString &originating, const QString &message); + void incomingHostInfo(const QString &servername, const QString &version, + const QString &userModes, const QString &channelModes); + void incomingYourHostInfo(const QString &servername, const QString &version, + const QString &userModes, const QString &channelModes); + void incomingConnectString(const QString &clients); + + //Channel Contact Signals + void incomingMessage(const QString &originating, const QString &target, const QString &message); + void incomingTopicChange(const QString &, const QString &, const QString &); + void incomingExistingTopic(const QString &, const QString &); + void incomingTopicUser(const QString &channel, const QString &user, const QDateTime &time); + void incomingJoinedChannel(const QString &channel,const QString &nick); + void incomingPartedChannel(const QString &channel,const QString &nick, const QString &reason); + void incomingNamesList(const QString &channel, const QStringList &nicknames); + void incomingEndOfNames(const QString &channel); + void incomingChannelMode(const QString &channel, const QString &mode, const QString ¶ms); + void incomingCannotSendToChannel(const QString &channel, const QString &message); + void incomingChannelModeChange(const QString &channel, const QString &nick, const QString &mode); + void incomingChannelHomePage(const QString &channel, const QString &url); + + //Contact Signals + void incomingPrivMessage(const QString &, const QString &, const QString &); + void incomingQuitIRC(const QString &user, const QString &reason); + void incomingUserModeChange(const QString &nick, const QString &mode); + void incomingNoSuchNickname(const QString &nick); + + // CTCP Signals +// void action(const QString &from, const QString &to, const QString &message); + void incomingAction(const QString &channel, const QString &originating, const QString &message); + void incomingPrivAction(const QString &target, const QString &originating, const QString &message); + + //Response Signals + void incomingUserOnline(const QString &nick); + void incomingWhoIsUser(const QString &nickname, const QString &username, + const QString &hostname, const QString &realname); + void incomingWhoWasUser(const QString &nickname, const QString &username, + const QString &hostname, const QString &realname); + void incomingWhoIsServer(const QString &nickname, const QString &server, const QString &serverInfo); + void incomingWhoIsOperator(const QString &nickname); + void incomingWhoIsIdentified(const QString &nickname); + void incomingWhoIsChannels(const QString &nickname, const QString &channel); + void incomingWhoIsIdle(const QString &nickname, unsigned long seconds); /* 317 */ + void incomingSignOnTime(const QString &nickname, unsigned long seconds); /* 317 */ + void incomingEndOfWhois(const QString &nickname); + void incomingEndOfWhoWas(const QString &nickname); + + void incomingWhoReply( const QString &nick, const QString &channel, const QString &user, const QString &host, + const QString &server,bool away, const QString &flag, uint hops, const QString &realName ); + + void incomingEndOfWho( const QString &query ); + + //Error Message Signals + void incomingServerLoadTooHigh(); + void incomingNickInUse(const QString &usingNick); + void incomingNickChange(const QString &, const QString &); + void incomingFailedServerPassword(); + void incomingFailedChankey(const QString &); + void incomingFailedChanBanned(const QString &); + void incomingFailedChanInvite(const QString &); + void incomingFailedChanFull(const QString &); + void incomingFailedNickOnLogin(const QString &); + void incomingNoNickChan(const QString &); + void incomingWasNoNick(const QString &); + + //General Signals + void incomingUnknown(const QString &); + void incomingUnknownCtcp(const QString &); + void incomingKick(const QString &channel, const QString &nick, + const QString &nickKicked, const QString &reason); + + void incomingUserIsAway(const QString &nick, const QString &awayMessage); + void incomingListedChan(const QString &chan, uint users, const QString &topic); + void incomingEndOfList(); + + void incomingCtcpReply(const QString &type, const QString &target, const QString &messageReceived); + +private slots: + void destroyed(KIRC::Entity *entity); + + void slotReadyRead(); + + void slotConnected(); + void slotConnectionClosed(); + void error(int errCode = 0); + + void ignoreMessage(KIRC::Message &msg); + void emitSuffix(KIRC::Message &); + + void error(KIRC::Message &msg); + void join(KIRC::Message &msg); + void kick(KIRC::Message &msg); + void mode(KIRC::Message &msg); + void nick(KIRC::Message &msg); + void notice(KIRC::Message &msg); + void part(KIRC::Message &msg); + void ping(KIRC::Message &msg); + void pong(KIRC::Message &msg); + void privmsg(KIRC::Message &msg); +// void squit(KIRC::Message &msg); + void quit(KIRC::Message &msg); + void topic(KIRC::Message &msg); + + void numericReply_001(KIRC::Message &msg); + void numericReply_002(KIRC::Message &msg); + void numericReply_003(KIRC::Message &msg); + void numericReply_004(KIRC::Message &msg); + void numericReply_005(KIRC::Message &msg); + void numericReply_250(KIRC::Message &msg); + void numericReply_251(KIRC::Message &msg); + void numericReply_252(KIRC::Message &msg); + void numericReply_253(KIRC::Message &msg); + void numericReply_254(KIRC::Message &msg); + void numericReply_255(KIRC::Message &msg); + void numericReply_263(KIRC::Message &msg); + void numericReply_265(KIRC::Message &msg); + void numericReply_266(KIRC::Message &msg); + void numericReply_301(KIRC::Message &msg); + void numericReply_303(KIRC::Message &msg); +// void numericReply_305(KIRC::Message &msg); +// void numericReply_306(KIRC::Message &msg); + void numericReply_307(KIRC::Message &msg); + void numericReply_311(KIRC::Message &msg); + void numericReply_312(KIRC::Message &msg); + void numericReply_313(KIRC::Message &msg); + void numericReply_314(KIRC::Message &msg); + void numericReply_315(KIRC::Message &msg); + void numericReply_317(KIRC::Message &msg); + void numericReply_318(KIRC::Message &msg); + void numericReply_319(KIRC::Message &msg); + void numericReply_320(KIRC::Message &msg); + void numericReply_322(KIRC::Message &msg); + void numericReply_323(KIRC::Message &msg); + void numericReply_324(KIRC::Message &msg); + void numericReply_328(KIRC::Message &msg); + void numericReply_329(KIRC::Message &msg); + void numericReply_331(KIRC::Message &msg); + void numericReply_332(KIRC::Message &msg); + void numericReply_333(KIRC::Message &msg); + void numericReply_352(KIRC::Message &msg); + void numericReply_353(KIRC::Message &msg); + void numericReply_366(KIRC::Message &msg); + void numericReply_369(KIRC::Message &msg); + void numericReply_372(KIRC::Message &msg); +// void numericReply_376(KIRC::Message &msg); + + void numericReply_401(KIRC::Message &msg); + void numericReply_406(KIRC::Message &msg); + void numericReply_422(KIRC::Message &msg); + void numericReply_433(KIRC::Message &msg); + void numericReply_464(KIRC::Message &msg); + void numericReply_471(KIRC::Message &msg); + void numericReply_473(KIRC::Message &msg); + void numericReply_474(KIRC::Message &msg); + void numericReply_475(KIRC::Message &msg); + + + void CtcpQuery_action(KIRC::Message &msg); + void CtcpQuery_clientinfo(KIRC::Message &msg); + void CtcpQuery_finger(KIRC::Message &msg); + void CtcpQuery_dcc(KIRC::Message &msg); + void CtcpQuery_ping(KIRC::Message &msg); + void CtcpQuery_source(KIRC::Message &msg); + void CtcpQuery_time(KIRC::Message &msg); + void CtcpQuery_userinfo(KIRC::Message &msg); + void CtcpQuery_version(KIRC::Message &msg); + + void CtcpReply_errmsg(KIRC::Message &msg); + void CtcpReply_ping(KIRC::Message &msg); + void CtcpReply_version(KIRC::Message &msg); + +private: + void bindCommands(); + void bindNumericReplies(); + void bindCtcp(); + + void setStatus(KIRC::Engine::Status status); + bool invokeCtcpCommandOfMessage(const QDict &map, KIRC::Message &message); + + /* + * Methods that handles all the bindings creations. + * This methods is used by all the bind(...) methods. + */ + bool _bind(QDict &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 m_entities; + KIRC::EntityPtr m_server; + KIRC::EntityPtr m_self; + + QString m_VersionString; + QString m_UserString; + QString m_SourceString; + QString m_PendingNick; + + QDict m_commands; +// QIntDict m_numericCommands; + QDict m_ctcpQueries; + QDict m_ctcpReplies; + + QMap customCtcpMap; + QDict 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 + Copyright (c) 2002 by Nick Betcher + Copyright (c) 2003 by Jason Keirstead + + Kopete (c) 2002-2003 by the Kopete developers + + ************************************************************************* + * * + * 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 + +#include + +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() + { + 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: "( *( "," ) [ *( "," ) ] ) / "0"" + * suspected: ": *(" "/"," )" + * assumed ":" + * 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. + * " *( "," ) *( "," ) []" + */ + 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. + * " *( ( "+" / "-" ) *( "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 + * "" */ + 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 + * " *( "," ) [ ]" + */ + 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: " " + * 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: " " + * 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. + * " [ ]" + */ + 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 + Copyright (c) 2002 by Nick Betcher + Copyright (c) 2003 by Jason Keirstead + + Kopete (c) 2002-2003 by the Kopete developers + + ************************************************************************* + * * + * 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 +#endif +#include +#include +#include + +#include +#include + +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 + Copyright (c) 2002 by Nick Betcher + Copyright (c) 2003 by Jason Keirstead + + Kopete (c) 2002-2003 by the Kopete developers + + ************************************************************************* + * * + * 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 + +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 !@" + * 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 , running version " + * 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 " + * 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: " " + * 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: ( clients) + * ( 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 users and services on 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: " :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: " :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: + * " :channels formed" */ +void Engine::numericReply_254(Message &msg) +{ + emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() ); +} + +/* 255: ":I have clients and 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: Max: " + * 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: Max: " + * Tells statistics about the current global(the whole irc server chain) server state: + */ +void Engine::numericReply_266(Message &msg) +{ + emit incomingConnectString( msg.suffix() ); +} + +/* 301: " :" + */ +void Engine::numericReply_301(Message &msg) +{ + emit incomingUserIsAway(Kopete::Message::unescape(msg.arg(1)), msg.suffix()); +} + +/* 303: ":*1 *(" " )" + */ +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: " * :" + * 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: " :" + * 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: " :is an IRC operator" + * Show info about an operator (part of a /whois). + */ +void Engine::numericReply_313(Message & /*msg*/) +{ +} + +/* 314: " * :" + * 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: " :seconds idle" + * Some servers say: " :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: "{} :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: + * " :{[@|+]}" + */ + 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: " :Users Name" ("Channel :Users Name") + * RFC1459: Declared. + * RFC2812: Obsoleted. + */ + +/* 322: " <# visible> :" + * 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: " " + */ +void Engine::numericReply_324(Message &msg) +{ + emit incomingChannelMode(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3)); +} + +/* 328: " " + */ +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: " :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: " :" + * 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 + * + * " ("H" / "G") ["*"] [("@" / "+")] : " + * + * :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: " :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: ":- " + * Part of the MOTD. + */ +void Engine::numericReply_372(Message &msg) +{ + emit incomingMotd(msg.suffix()); +} + +/* 375: ":- 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: " :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: " :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: " :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: " :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 + + Kopete (c) 2004 by the Kopete developers + + ************************************************************************* + * * + * 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 + +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 + + Kopete (c) 2004 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include + +#include +#include +#include +#include + +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 +{ +public: + EntityPtr(KIRC::Entity *entity = 0) + : KSharedPtr(entity) + { } + + EntityPtr(const KIRC::EntityPtr &entity) + : KSharedPtr(entity) + { } +}; + +class EntityPtrList + : public QValueList +{ +public: + EntityPtrList() + { } + + EntityPtrList(const EntityPtr &entity) + { + append(entity); + } + + EntityPtrList(const QValueList &list) + : QValueList(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 + + Kopete (c) 2003 by the Kopete engineelopers + + ************************************************************************* + * * + * 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 +#include +#include +#include + +using namespace KIRC; + +#ifndef _IRC_STRICTNESS_ +QRegExp Message::m_IRCNumericCommand("^\\d{1,3}$"); + +// TODO: This regexp parsing is no good. It's slower than it needs to be, and +// is not codec-safe since QString requires a codec. NEed to parse this with +// our own parsing class that operates on the raw QCStrings +QRegExp Message::m_IRCCommandType1( + "^(?::([^ ]+) )?([A-Za-z]+|\\d{1,3})((?: [^ :][^ ]*)*) ?(?: :(.*))?$"); + // Extra end arg space check -------------------------^ +#else // _IRC_STRICTNESS_ +QRegExp Message::m_IRCNumericCommand("^\\d{3,3}$"); + +QRegExp Message::m_IRCCommandType1( + "^(?::([^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){0,13})(?: :(.*))?$"); +QRegExp Message::m_IRCCommandType2( + "^(?::[[^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){14,14})(?: (.*))?$"); +#endif // _IRC_STRICTNESS_ + +Message::Message() + : m_ctcpMessage(0) +{ +} + +Message::Message(const Message &obj) + : m_ctcpMessage(0) +{ + m_raw = obj.m_raw; + + m_prefix = obj.m_prefix; + m_command = obj.m_command; + m_args = obj.m_args; + m_suffix = obj.m_suffix; + + m_ctcpRaw = obj.m_ctcpRaw; + + if (obj.m_ctcpMessage) + m_ctcpMessage = new Message(obj.m_ctcpMessage); +} + +Message::Message(const Message *obj) + : m_ctcpMessage(0) +{ + m_raw = obj->m_raw; + + m_prefix = obj->m_prefix; + m_command = obj->m_command; + m_args = obj->m_args; + m_suffix = obj->m_suffix; + + m_ctcpRaw = obj->m_ctcpRaw; + + if (obj->m_ctcpMessage) + m_ctcpMessage = new Message(obj->m_ctcpMessage); +} + +Message::~Message() +{ + if (m_ctcpMessage) + delete m_ctcpMessage; +} + +void Message::writeRawMessage(Engine *engine, const QTextCodec *codec, const QString &str) +{ + // FIXME: Really handle this + if (!engine->socket()) + { + kdDebug(14121) << k_funcinfo << "Not connected while attempting to write:" << str << endl; + return; + } + + QString txt = str + QString::fromLatin1("\r\n"); + + QCString s(codec->fromUnicode(txt)); + kdDebug(14120) << "Message is " << s.length() << " chars" << endl; + // FIXME: Should check the amount of data really writen. + int wrote = engine->socket()->writeBlock(s.data(), s.length()); + + kdDebug(14121) << QString::fromLatin1("(%1 bytes) >> %2").arg(wrote).arg(str) << endl; +} + +void Message::writeMessage(Engine *engine, const QTextCodec *codec, const QString &message) +{ + writeRawMessage(engine, codec, quote(message)); +} + +void Message::writeMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QStringList &args, const QString &suffix) +{ + QString msg = command; + + if (!args.isEmpty()) + msg += QChar(' ') + args.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here + + if (!suffix.isNull()) + msg = msg.stripWhiteSpace() + QString::fromLatin1(" :") + suffix; + + writeMessage(engine, codec, msg); +} + +void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QString&to, + const QString &ctcpMessage) +{ + writeMessage(engine, codec, command, to, QChar(0x01) + ctcpQuote(ctcpMessage) + QChar(0x01)); +} + +void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix ) +{ + QString ctcpMsg = ctcpCommand; + + if (!ctcpArgs.isEmpty()) + ctcpMsg += QChar(' ') + ctcpArgs.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here + + if (!ctcpSuffix.isNull()) + ctcpMsg += QString::fromLatin1(" :") + ctcpSuffix; + + writeMessage(engine, codec, command, to, suffix + QChar(0x01) + ctcpQuote(ctcpMsg) + QChar(0x01)); +} + +Message Message::parse(Engine *engine, const QTextCodec *codec, bool *parseSuccess) +{ + if (parseSuccess) + *parseSuccess=false; + + if (engine->socket()->canReadLine()) + { + QCString raw(engine->socket()->bytesAvailable()+1); + Q_LONG length = engine->socket()->readLine(raw.data(), raw.count()); + + if( length > -1 ) + { + raw.resize( length ); + + // Remove trailing '\r\n' or '\n'. + // + // Some servers send '\n' instead of '\r\n' that the RFCs say they should be sending. + + if (length > 1 && raw.at(length-2) == '\n') { + raw.at(length-2) = '\0'; + } + if (length > 2 && raw.at(length-3) == '\r') { + raw.at(length-3) = '\0'; + } + + kdDebug(14121) << "<< " << raw << endl; + + Message msg; + if(matchForIRCRegExp(raw, codec, msg)) + { + if(parseSuccess) + *parseSuccess = true; + } + else + { + kdDebug(14120) << k_funcinfo << "Unmatched line: \"" << raw << "\"" << endl; + } + + return msg; + } + else + kdWarning(14121) << k_funcinfo << "Failed to read a line while canReadLine returned true!" << endl; + } + + return Message(); +} + +QString Message::quote(const QString &str) +{ + QString tmp = str; + QChar q('\020'); + tmp.replace(q, q+QString(q)); + tmp.replace(QChar('\r'), q+QString::fromLatin1("r")); + tmp.replace(QChar('\n'), q+QString::fromLatin1("n")); + tmp.replace(QChar('\0'), q+QString::fromLatin1("0")); + return tmp; +} + +// FIXME: The unquote system is buggy. +QString Message::unquote(const QString &str) +{ + QString tmp = str; + + char b[3] = { 020, 020, '\0' }; + const char b2[2] = { 020, '\0' }; + + tmp.replace( b, b2 ); + b[1] = 'r'; + tmp.replace( b, "\r"); + b[1] = 'n'; + tmp.replace( b, "\n"); + b[1] = '0'; + tmp.replace( b, "\0"); + + return tmp; +} + +QString Message::ctcpQuote(const QString &str) +{ + QString tmp = str; + tmp.replace( QChar('\\'), QString::fromLatin1("\\\\")); + tmp.replace( (char)1, QString::fromLatin1("\\1")); + return tmp; +} + +QString Message::ctcpUnquote(const QString &str) +{ + QString tmp = str; + tmp.replace("\\\\", "\\"); + tmp.replace("\\1", "\1" ); + return tmp; +} + +bool Message::matchForIRCRegExp(const QCString &line, const QTextCodec *codec, Message &message) +{ + if(matchForIRCRegExp(m_IRCCommandType1, codec, line, message)) + return true; +#ifdef _IRC_STRICTNESS_ + if(!matchForIRCRegExp(m_IRCCommandType2, codec, line, message)) + return true; +#endif // _IRC_STRICTNESS_ + return false; +} + +// FIXME: remove the decodeStrings calls or update them. +// FIXME: avoid the recursive call, it make the ctcp command unquoted twice (wich is wrong, but valid in most of the cases) +bool Message::matchForIRCRegExp(QRegExp ®exp, const QTextCodec *codec, const QCString &line, Message &msg ) +{ + if( regexp.exactMatch( codec->toUnicode(line) ) ) + { + msg.m_raw = line; + msg.m_prefix = unquote(regexp.cap(1)); + msg.m_command = unquote(regexp.cap(2)); + msg.m_args = QStringList::split(' ', regexp.cap(3)); + + QCString suffix = codec->fromUnicode(unquote(regexp.cap(4))); + if (!suffix.isNull() && suffix.length() > 0) + { + QCString ctcpRaw; + if (extractCtcpCommand(suffix, ctcpRaw)) + { + msg.m_ctcpRaw = codec->toUnicode(ctcpRaw); + + msg.m_ctcpMessage = new Message(); + msg.m_ctcpMessage->m_raw = codec->fromUnicode(ctcpUnquote(msg.m_ctcpRaw)); + + int space = ctcpRaw.find(' '); + if (!matchForIRCRegExp(msg.m_ctcpMessage->m_raw, codec, *msg.m_ctcpMessage)) + { + QCString command; + if (space > 0) + command = ctcpRaw.mid(0, space).upper(); + else + command = ctcpRaw.upper(); + msg.m_ctcpMessage->m_command = + Kopete::Message::decodeString( KSParser::parse(command), codec ); + } + + if (space > 0) + msg.m_ctcpMessage->m_ctcpRaw = + Kopete::Message::decodeString( KSParser::parse(ctcpRaw.mid(space)), codec ); + } + + msg.m_suffix = Kopete::Message::decodeString( KSParser::parse(suffix), codec ); + } + else + msg.m_suffix = QString::null; + return true; + } + return false; +} + +void Message::decodeAgain( const QTextCodec *codec ) +{ + matchForIRCRegExp(m_raw, codec, *this); +} + +// FIXME: there are missing parts +QString Message::toString() const +{ + if( !isValid() ) + return QString::null; + + QString msg = m_command; + for (QStringList::ConstIterator it = m_args.begin(); it != m_args.end(); ++it) + msg += QChar(' ') + *it; + if (!m_suffix.isNull()) + msg += QString::fromLatin1(" :") + m_suffix; + + return msg; +} + +bool Message::isNumeric() const +{ + return m_IRCNumericCommand.exactMatch(m_command); +} + +bool Message::isValid() const +{ +// This could/should be more complex but the message validity is tested durring the parsing +// So this is enougth as we don't allow the editing the content. + return !m_command.isEmpty(); +} + +/* Return true if the given string is a special command string + * (i.e start and finish with the ascii code \001), and the given + * string is splited to get the first part of the message and fill the ctcp command. + * FIXME: The code currently only match for a textual message or a ctcp message not both mixed as it can be (even if very rare). + */ +bool Message::extractCtcpCommand(QCString &message, QCString &ctcpline) +{ + uint len = message.length(); + + if( message[0] == 1 && message[len-1] == 1 ) + { + ctcpline = message.mid(1,len-2); + message.truncate(0); + + return true; + } + + return false; +} + +void Message::dump() const +{ + kdDebug(14120) << "Raw:" << m_raw << endl + << "Prefix:" << m_prefix << endl + << "Command:" << m_command << endl + << "Args:" << m_args << endl + << "Suffix:" << m_suffix << endl + << "CtcpRaw:" << m_ctcpRaw << endl; + if(m_ctcpMessage) + { + kdDebug(14120) << "Contains CTCP Message:" << endl; + m_ctcpMessage->dump(); + } +} diff --git a/kopete/protocols/irc/libkirc/kircmessage.h b/kopete/protocols/irc/libkirc/kircmessage.h new file mode 100644 index 00000000..e37f3fb2 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessage.h @@ -0,0 +1,198 @@ +/* + kircmessage.h - IRC Client + + Copyright (c) 2003 by Michel Hermier + + Kopete (c) 2003 by the Kopete developers + + ************************************************************************* + * * + * 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 + +#include +#include +#include +#include +#include + +#include + +// Uncoment this if you want a really rfc compliant message handling. +// This is due to some changes of the message encoding with 14 arguments.(not very frequent :) +// #define _IRC_STRICTNESS_ + +namespace KIRC +{ + +class Engine; + +class Message +{ +public: + /** \brief Sends the message as-is to the server. + */ + static void writeRawMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str); + + static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str); + + static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QStringList &args, const QString &suffix); + + static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, + const QString &ctcpMessage); + + static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec, + const QString &command, const QString &to, const QString &suffix, + const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null ); + + Message(); + Message(const KIRC::Message &obj); + Message(const KIRC::Message *obj); + + ~Message(); + + inline const QString nickFromPrefix() const + { return Kopete::Message::unescape(KIRC::Entity::userNick(m_prefix)); } + + QString toString() const; + + /** \brief Returns true if the message command is numeric. + */ + bool isNumeric() const; + + /** \brief Message is valid if it was parsed correctly. + */ + bool isValid() const; + + /** \brief Writes internal message information about this message through kdDebug(). + */ + void dump() const; + + /** \brief Re-decodes the message with given codec. + */ + void decodeAgain( const QTextCodec *codec ); + + /** \brief The whole message as received. + */ + inline const QCString &raw() const + { return m_raw; } + + /** \brief Prefix of this message. + * + * Returns the prefix of the message. Note that it can be empty. + * + * Prefix is the server name or the nick name of the sender. + * + * message = [ ":" prefix SPACE ] command [ params ] crlf + * prefix = servername / ( nickname [ [ "!" user ] "@" host ] ) + */ + inline const QString &prefix() const + { return m_prefix; } + + /** \brief The command part of this message. + * + * Returns the command of this message. Can be numerical. + * + * Examples: "MODE", "PRIVMSG", 303, 001, ... + */ + inline const QString &command() const + { return m_command; } + + /** \brief The number of command arguments this message contains. + */ + inline size_t argsSize() const + { return m_args.size(); } + + /** \brief i:th command argument. + */ + inline const QString &arg(size_t i) const + { return m_args[i]; } + + /** \brief All command arguments. + */ + inline const QStringList &args() const + { return m_args; } + + /** \brief Message suffix. + */ + inline const QString &suffix() const + { return m_suffix; } + inline const QString &ctcpRaw() const + { return m_ctcpRaw; } + + inline bool hasCtcpMessage() const + { return m_ctcpMessage!=0; } + inline class KIRC::Message &ctcpMessage() const + { return *m_ctcpMessage; } + + static KIRC::Message parse(KIRC::Engine *engine, const QTextCodec *codec, bool *parseSuccess=0); + +private: + /** + * Contains the low level dequoted message. + */ + QCString m_raw; + + /** + * Contains the completely dequoted prefix. + */ + QString m_prefix; + /** + * Contains the completely dequoted command. + */ + QString m_command; + /** + * Contains the completely dequoted args. + */ + QStringList m_args; + /** + * Contains the completely dequoted suffix. + */ + QString m_suffix; + + /** + * If it is a message contains the completely dequoted rawCtcpLine. + * If it is a ctcp message contains the completely dequoted rawCtcpArgsLine. + */ + QString m_ctcpRaw; + + // low level quoting, message quoting + static QString quote(const QString &str); + static QString unquote(const QString &str); + + // ctcp level quoting + static QString ctcpQuote(const QString &str); + static QString ctcpUnquote(const QString &str); + + static bool extractCtcpCommand(QCString &str, QCString &ctcpline); + + static bool matchForIRCRegExp(const QCString &line, const QTextCodec *codec, KIRC::Message &message); + static bool matchForIRCRegExp(QRegExp ®exp, const QTextCodec *codec, const QCString &line, KIRC::Message &message); + + class KIRC::Message *m_ctcpMessage; + + static QRegExp m_IRCCommandType1; + #ifdef _IRC_STRICTNESS_ + static QRegExp m_IRCCommandType2; + #endif // _IRC_STRICTNESS_ + + static QRegExp m_IRCNumericCommand; +}; + +} + +#endif // KIRCMESSAGE_H diff --git a/kopete/protocols/irc/libkirc/kircmessageredirector.cpp b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp new file mode 100644 index 00000000..49194ce0 --- /dev/null +++ b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp @@ -0,0 +1,97 @@ +/* + kircmessageredirector.cpp - IRC Client + + Copyright (c) 2004 by Michel Hermier + + Kopete (c) 2004 by the Kopete developers + + ************************************************************************* + * * + * 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 + + Kopete (c) 2004 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include + +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 + + Kopete (c) 2003-2004 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include + +#include +#include + +#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 + + Kopete (c) 2003 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include +#include +#include + +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 + + Kopete (c) 2003 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include + +#include +#include +#include + +#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 + + Kopete (c) 2003-2004 by the Kopete developers + + ************************************************************************* + * * + * 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 + +#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 getRegisteredServers(); +// static QPtrList getAllRegisteredServers(); +// void unregisterServer( DCCServer * ); + +// void registerClient( DCCClient * ); +// QPtrList getRegisteredClients(); +// static QPtrList getAllRegisteredClients(); +// void unregisterClient( DCCClient * ); + +signals: + void transferServerCreated(KIRC::TransferServer *server); + void transferCreated(KIRC::Transfer *transfer); + +private: +// TransferHandler(); + + TransferServer *m_server; +// QPtrList m_servers; +// QPtrList 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 + + Kopete (c) 2003 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include + +#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(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 + + Kopete (c) 2003-2004 by the Kopete developers + + ************************************************************************* + * * + * 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 + +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 m_pendingTransfers; + // QPtrList 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 + Copyright (c) 2004 by Jason Keirstead + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ksslsocket.h" + +struct KSSLSocketPrivate +{ + mutable KSSL *kssl; + KSSLCertificateCache *cc; + DCOPClient *dcc; + QMap 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 + * Copyright (C) 2001 Dawit Alemayehu +*/ + +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 chain = pc.chain().getChain(); + for (KSSLCertificate *c = chain.first(); c; c = chain.next()) + { + theChain += c->toString(); + theChain += "\n"; + } + setMetaData("ssl_peer_chain", theChain); + } + else + { + setMetaData("ssl_peer_chain", ""); + } + + setMetaData("ssl_cert_state", QString::number(ksv)); + + if (ksv == KSSLCertificate::Ok) + { + rc = 1; + setMetaData("ssl_action", "accept"); + } + + // Since we're the parent, we need to teach the child. + setMetaData("ssl_parent_ip", ourIp ); + setMetaData("ssl_parent_cert", pc.toString()); + + // - Read from cache and see if there is a policy for this + KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(pc); + + // - validation code + if (ksv != KSSLCertificate::Ok) + { + if( cp == KSSLCertificateCache::Unknown || cp == KSSLCertificateCache::Ambiguous) + { + cp = KSSLCertificateCache::Prompt; + } + else + { + // A policy was already set so let's honor that. + permacache = d->cc->isPermanent(pc); + } + + if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) + { + cp = KSSLCertificateCache::Prompt; + } + + // Precondition: cp is one of Reject, Accept or Prompt + switch (cp) + { + case KSSLCertificateCache::Accept: + rc = 1; + break; + + case KSSLCertificateCache::Reject: + rc = -1; + break; + + case KSSLCertificateCache::Prompt: + { + do + { + if (ksv == KSSLCertificate::InvalidHost) + { + QString msg = i18n("The IP address of the host %1 " + "does not match the one the " + "certificate was issued to."); + result = messageBox( KIO::SlaveBase::WarningYesNoCancel, + msg.arg(ourHost), + i18n("Server Authentication"), + i18n("&Details"), + i18n("Co&ntinue") ); + } + else + { + QString msg = i18n("The server certificate failed the " + "authenticity test (%1)."); + result = messageBox( KIO::SlaveBase::WarningYesNoCancel, + msg.arg(ourHost), + i18n("Server Authentication"), + i18n("&Details"), + i18n("Co&ntinue") ); + } + + if (result == KMessageBox::Yes) + { + showInfoDialog(); + } + } + while (result == KMessageBox::Yes); + + if (result == KMessageBox::No) + { + rc = 1; + cp = KSSLCertificateCache::Accept; + doAddHost = true; + result = messageBox( KIO::SlaveBase::WarningYesNo, + i18n("Would you like to accept this " + "certificate forever without " + "being prompted?"), + i18n("Server Authentication"), + i18n("&Forever"), + i18n("&Current Sessions Only")); + if (result == KMessageBox::Yes) + permacache = true; + else + permacache = false; + } + else + { + rc = -1; + cp = KSSLCertificateCache::Prompt; + } + + break; + } + default: + kdDebug(14120) << "SSL error in cert code." + << "Please report this to kopete-devel@kde.org." + << endl; + break; + } + } + + // - cache the results + d->cc->addCertificate(pc, cp, permacache); + if (doAddHost) + d->cc->addHost(pc, ourHost); + + + if (rc == -1) + return rc; + + + kdDebug(14120) << "SSL connection information follows:" << endl + << "+-----------------------------------------------" << endl + << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl + << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl + << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl + << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits() + << " of " << d->kssl->connectionInfo().getCipherBits() + << " bits used." << endl + << "| PEER:" << endl + << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl + << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl + << "| Validation: " << (int)ksv << endl + << "| Certificate matches IP: " << _IPmatchesCN << endl + << "+-----------------------------------------------" + << endl; + + // sendMetaData(); Do not call this function!! + return rc; +} + + +#include "ksslsocket.moc" diff --git a/kopete/protocols/irc/libkirc/ksslsocket.h b/kopete/protocols/irc/libkirc/ksslsocket.h new file mode 100644 index 00000000..692d5288 --- /dev/null +++ b/kopete/protocols/irc/libkirc/ksslsocket.h @@ -0,0 +1,68 @@ + +#ifndef _K_SSL_SOCKET_H_ +#define _K_SSL_SOCKET_H_ + +/* + ksslsocket.h - KDE SSL Socket + + Copyright (c) 2005 by Tommi Rantala + Copyright (c) 2004 by Jason Keirstead + + Kopete (c) 2002-2005 by the Kopete developers + + ************************************************************************* + * * + * 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 +#include +#include + +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 -- cgit v1.2.1