From e008cd12ef319c4b5f9fe5a8cc1524829551771d Mon Sep 17 00:00:00 2001 From: Manuel Nickschas Date: Tue, 25 Dec 2007 00:36:10 +0000 Subject: [PATCH] My X-Mas present to you: partially working encodings! \o/ Actually, most of the basic infrastructure is in place; but we can't configure it yet, i.e. ISO-8859-15 it is for now, and encodings are not respected when we send stuff yet. It instantly gives you a working utf8 detection on receiving though, so those crappy umlauts should be a thing of the past now. The infrastructure allows for setting individual encodings for networks, channels and even IRC users (for queries), so putting a decent UI on that thing should make Quassel really useful in international environments that are too backward to use utf8 yet :) BTW, hacking on Christmas Eve proves to be really productive... --- Quassel.kdevelop.filelist | 1 - src/client/client.cpp | 6 +- src/common/bufferinfo.cpp | 2 +- src/common/ircchannel.cpp | 38 +++++ src/common/ircchannel.h | 13 ++ src/common/ircuser.cpp | 41 +++++- src/common/ircuser.h | 27 +++- src/common/networkinfo.cpp | 40 ++++- src/common/networkinfo.h | 21 ++- src/common/signalproxy.h | 12 +- src/common/util.cpp | 16 +- src/common/util.h | 26 ++-- src/core/core.cpp | 10 +- src/core/ctcphandler.h | 6 +- src/core/ircserverhandler.cpp | 266 +++++++++++++++++++--------------- src/core/ircserverhandler.h | 55 ++++--- src/core/server.cpp | 35 +++++ src/core/server.h | 20 ++- 18 files changed, 441 insertions(+), 194 deletions(-) diff --git a/Quassel.kdevelop.filelist b/Quassel.kdevelop.filelist index dfd181fe..f6c3b0b8 100644 --- a/Quassel.kdevelop.filelist +++ b/Quassel.kdevelop.filelist @@ -161,7 +161,6 @@ src/qtui/serverlist.cpp src/qtui/serverlist.h src/qtui/settingsdlg.cpp src/qtui/settingsdlg.h -src/qtui/settingspage.h src/qtui/settingspages.cpp src/qtui/settingspages.h src/qtui/topicwidget.cpp diff --git a/src/client/client.cpp b/src/client/client.cpp index 28c656e9..e78fc17e 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -243,7 +243,7 @@ void Client::disconnectFromCore() { } void Client::setCoreConfiguration(const QVariantMap &settings) { - writeDataToDevice(socket, settings); + SignalProxy::writeDataToDevice(socket, settings); } void Client::coreSocketConnected() { @@ -253,7 +253,7 @@ void Client::coreSocketConnected() { clientInit["GuiProtocol"] = GUI_PROTOCOL; clientInit["User"] = coreConnectionInfo["User"].toString(); clientInit["Password"] = coreConnectionInfo["Password"].toString(); - writeDataToDevice(socket, clientInit); + SignalProxy::writeDataToDevice(socket, clientInit); } void Client::coreSocketDisconnected() { @@ -418,7 +418,7 @@ void Client::coreSocketError(QAbstractSocket::SocketError) { void Client::coreHasData() { QVariant item; - if(readDataFromDevice(socket, blockSize, item)) { + if(SignalProxy::readDataFromDevice(socket, blockSize, item)) { emit recvPartialItem(1,1); QVariantMap msg = item.toMap(); if (!msg["StartWizard"].toBool()) { diff --git a/src/common/bufferinfo.cpp b/src/common/bufferinfo.cpp index 2eb790dc..a68101cf 100644 --- a/src/common/bufferinfo.cpp +++ b/src/common/bufferinfo.cpp @@ -47,7 +47,7 @@ QString BufferInfo::buffer() const { if(isChannelName(_bufferName)) return _bufferName; else - return nickFromMask(_bufferName); + return nickFromMask(_bufferName); // FIXME get rid of global functions and use the NetworkInfo stuff instead! } QDebug operator<<(QDebug dbg, const BufferInfo &b) { diff --git a/src/common/ircchannel.cpp b/src/common/ircchannel.cpp index 47082dd6..9f1a91f3 100644 --- a/src/common/ircchannel.cpp +++ b/src/common/ircchannel.cpp @@ -24,9 +24,11 @@ //#include "nicktreemodel.h" #include "signalproxy.h" #include "ircuser.h" +#include "util.h" #include #include +#include #include @@ -98,6 +100,42 @@ QString IrcChannel::userModes(const QString &nick) const { return userModes(networkInfo->ircUser(nick)); } +QTextCodec *IrcChannel::codecForEncoding() const { + return _codecForEncoding; +} + +void IrcChannel::setCodecForEncoding(const QString &name) { + setCodecForEncoding(QTextCodec::codecForName(name.toAscii())); +} + +void IrcChannel::setCodecForEncoding(QTextCodec *codec) { + _codecForEncoding = codec; +} + +QTextCodec *IrcChannel::codecForDecoding() const { + return _codecForDecoding; +} + +void IrcChannel::setCodecForDecoding(const QString &name) { + setCodecForDecoding(QTextCodec::codecForName(name.toAscii())); +} + +void IrcChannel::setCodecForDecoding(QTextCodec *codec) { + _codecForDecoding = codec; +} + +QString IrcChannel::decodeString(const QByteArray &text) const { + if(!codecForDecoding()) return networkInfo->decodeString(text); + return ::decodeString(text, _codecForDecoding); +} + +QByteArray IrcChannel::encodeString(const QString string) const { + if(codecForEncoding()) { + return _codecForEncoding->fromUnicode(string); + } + return networkInfo->encodeString(string); +} + // ==================== // PUBLIC SLOTS: // ==================== diff --git a/src/common/ircchannel.h b/src/common/ircchannel.h index 521e1a12..a499b24c 100644 --- a/src/common/ircchannel.h +++ b/src/common/ircchannel.h @@ -53,6 +53,16 @@ public: QString userModes(IrcUser *ircuser) const; QString userModes(const QString &nick) const; + QTextCodec *codecForEncoding() const; + QTextCodec *codecForDecoding() const; + void setCodecForEncoding(const QString &codecName); + void setCodecForEncoding(QTextCodec *codec); + void setCodecForDecoding(const QString &codecName); + void setCodecForDecoding(QTextCodec *codec); + + QString decodeString(const QByteArray &text) const; + QByteArray encodeString(const QString string) const; + public slots: void setTopic(const QString &topic); @@ -109,6 +119,9 @@ private: QHash _userModes; NetworkInfo *networkInfo; + + QTextCodec *_codecForEncoding; + QTextCodec *_codecForDecoding; }; #endif diff --git a/src/common/ircuser.cpp b/src/common/ircuser.cpp index 333ee93b..91b3a2b2 100644 --- a/src/common/ircuser.cpp +++ b/src/common/ircuser.cpp @@ -25,6 +25,7 @@ #include "signalproxy.h" #include "ircchannel.h" +#include #include IrcUser::IrcUser(const QString &hostmask, NetworkInfo *networkinfo) @@ -33,7 +34,9 @@ IrcUser::IrcUser(const QString &hostmask, NetworkInfo *networkinfo) _nick(nickFromMask(hostmask)), _user(userFromMask(hostmask)), _host(hostFromMask(hostmask)), - networkInfo(networkinfo) + networkInfo(networkinfo), + _codecForEncoding(0), + _codecForDecoding(0) { updateObjectName(); } @@ -78,6 +81,42 @@ QStringList IrcUser::channels() const { return chanList; } +QTextCodec *IrcUser::codecForEncoding() const { + return _codecForEncoding; +} + +void IrcUser::setCodecForEncoding(const QString &name) { + setCodecForEncoding(QTextCodec::codecForName(name.toAscii())); +} + +void IrcUser::setCodecForEncoding(QTextCodec *codec) { + _codecForEncoding = codec; +} + +QTextCodec *IrcUser::codecForDecoding() const { + return _codecForDecoding; +} + +void IrcUser::setCodecForDecoding(const QString &name) { + setCodecForDecoding(QTextCodec::codecForName(name.toAscii())); +} + +void IrcUser::setCodecForDecoding(QTextCodec *codec) { + _codecForDecoding = codec; +} + +QString IrcUser::decodeString(const QByteArray &text) const { + if(!codecForDecoding()) return networkInfo->decodeString(text); + return ::decodeString(text, codecForDecoding()); +} + +QByteArray IrcUser::encodeString(const QString string) const { + if(codecForEncoding()) { + return codecForEncoding()->fromUnicode(string); + } + return networkInfo->encodeString(string); +} + // ==================== // PUBLIC SLOTS: // ==================== diff --git a/src/common/ircuser.h b/src/common/ircuser.h index e933fb61..33dd1432 100644 --- a/src/common/ircuser.h +++ b/src/common/ircuser.h @@ -40,7 +40,6 @@ class IrcUser : public QObject { Q_PROPERTY(QStringList channels READ channels STORED false) // Q_PROPERTY(QStringList usermodes READ usermodes WRITE setUsermodes) - public: IrcUser(const QString &hostmask, NetworkInfo *networkInfo); virtual ~IrcUser(); @@ -56,6 +55,17 @@ public: QStringList channels() const; + // user-specific encodings + QTextCodec *codecForEncoding() const; + QTextCodec *codecForDecoding() const; + void setCodecForEncoding(const QString &codecName); + void setCodecForEncoding(QTextCodec *codec); + void setCodecForDecoding(const QString &codecName); + void setCodecForDecoding(QTextCodec *codec); + + QString decodeString(const QByteArray &text) const; + QByteArray encodeString(const QString string) const; + public slots: void setUser(const QString &user); void setHost(const QString &host); @@ -63,7 +73,7 @@ public slots: void updateHostmask(const QString &mask); void setUserModes(const QString &modes); - + void joinChannel(IrcChannel *channel); void joinChannel(const QString &channelname); void partChannel(IrcChannel *channel); @@ -82,9 +92,9 @@ signals: void hostSet(QString host); void nickSet(QString newnick); void hostmaskUpdated(QString mask); - + void userModesSet(QString modes); - + void channelJoined(QString channel); void channelParted(QString channel); @@ -92,7 +102,7 @@ signals: void userModeRemoved(QString mode); void renameObject(QString oldname, QString newname); - + // void setUsermodes(const QSet &usermodes); // QSet usermodes() const; @@ -101,7 +111,7 @@ signals: private slots: void updateObjectName(); void channelDestroyed(); - + private: inline bool operator==(const IrcUser &ircuser2) { return (_nick.toLower() == ircuser2.nick().toLower()); @@ -120,8 +130,11 @@ private: // QSet _channels; QSet _channels; QString _userModes; - + NetworkInfo *networkInfo; + + QTextCodec *_codecForEncoding; + QTextCodec *_codecForDecoding; }; #endif diff --git a/src/common/networkinfo.cpp b/src/common/networkinfo.cpp index 89e88ccd..62dacace 100644 --- a/src/common/networkinfo.cpp +++ b/src/common/networkinfo.cpp @@ -24,6 +24,7 @@ #include "ircchannel.h" #include +#include #include "util.h" @@ -39,7 +40,9 @@ NetworkInfo::NetworkInfo(const uint &networkid, QObject *parent) _currentServer(QString()), _prefixes(QString()), _prefixModes(QString()), - _proxy(NULL) + _proxy(0), + _codecForEncoding(0), + _codecForDecoding(0) { setObjectName(QString::number(networkid)); } @@ -244,6 +247,41 @@ QList NetworkInfo::ircChannels() const { return _ircChannels.values(); } +QTextCodec *NetworkInfo::codecForEncoding() const { + return _codecForEncoding; +} + +void NetworkInfo::setCodecForEncoding(const QString &name) { + setCodecForEncoding(QTextCodec::codecForName(name.toAscii())); +} + +void NetworkInfo::setCodecForEncoding(QTextCodec *codec) { + _codecForEncoding = codec; +} + +QTextCodec *NetworkInfo::codecForDecoding() const { + return _codecForDecoding; +} + +void NetworkInfo::setCodecForDecoding(const QString &name) { + setCodecForDecoding(QTextCodec::codecForName(name.toAscii())); +} + +void NetworkInfo::setCodecForDecoding(QTextCodec *codec) { + _codecForDecoding = codec; +} + +QString NetworkInfo::decodeString(const QByteArray &text) const { + return ::decodeString(text, _codecForDecoding); +} + +QByteArray NetworkInfo::encodeString(const QString string) const { + if(_codecForEncoding) { + return _codecForEncoding->fromUnicode(string); + } + return string.toAscii(); +} + // ==================== // Public Slots: // ==================== diff --git a/src/common/networkinfo.h b/src/common/networkinfo.h index 0c6516bd..1f712fbb 100644 --- a/src/common/networkinfo.h +++ b/src/common/networkinfo.h @@ -51,7 +51,7 @@ public: SignalProxy *proxy() const; void setProxy(SignalProxy *proxy); - + bool isMyNick(const QString &nick) const; bool isMyNick(IrcUser *ircuser) const; @@ -61,7 +61,7 @@ public: QString prefixToMode(const QCharRef &prefix); QString modeToPrefix(const QString &mode); QString modeToPrefix(const QCharRef &mode); - + QString networkName() const; QString currentServer() const; QString myNick() const; @@ -73,15 +73,25 @@ public: bool supports(const QString ¶m) const; QString support(const QString ¶m) const; - + IrcUser *newIrcUser(const QString &hostmask); IrcUser *ircUser(QString nickname) const; QList ircUsers() const; - + IrcChannel *newIrcChannel(const QString &channelname); IrcChannel *ircChannel(QString channelname); QList ircChannels() const; + QTextCodec *codecForEncoding() const; + QTextCodec *codecForDecoding() const; + void setCodecForEncoding(const QString &codecName); + void setCodecForEncoding(QTextCodec *codec); + void setCodecForDecoding(const QString &codecName); + void setCodecForDecoding(QTextCodec *codec); + + QString decodeString(const QByteArray &text) const; + QByteArray encodeString(const QString string) const; + public slots: void setNetworkName(const QString &networkName); void setCurrentServer(const QString ¤tServer); @@ -153,6 +163,9 @@ private: QPointer _proxy; void determinePrefixes(); + QTextCodec *_codecForEncoding; + QTextCodec *_codecForDecoding; + }; #endif diff --git a/src/common/signalproxy.h b/src/common/signalproxy.h index b787c2ad..be2c9ffd 100644 --- a/src/common/signalproxy.h +++ b/src/common/signalproxy.h @@ -72,8 +72,18 @@ public: void detachObject(QObject *obj); void detachSignals(QObject *sender); void detachSlots(QObject *receiver); - + + //! Writes a QVariant to a device. + /** The data item is prefixed with the resulting blocksize, + * so the corresponding function readDataFromDevice() can check if enough data is available + * at the device to reread the item. + */ static void writeDataToDevice(QIODevice *dev, const QVariant &item); + + //! Reads a data item from a device that has been written by writeDataToDevice(). + /** If not enough data bytes are available, the function returns false and the QVariant reference + * remains untouched. + */ static bool readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item); static QString methodBaseName(const QMetaMethod &method); diff --git a/src/common/util.cpp b/src/common/util.cpp index db1c6da4..25ff475b 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005/06 by the Quassel IRC Team * + * Copyright (C) 2005-07 by the Quassel IRC Team * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -44,11 +44,11 @@ bool isChannelName(QString str) { return QString("#&!+").contains(str[0]); } -QString decodeString(QByteArray input, QString encoding) { +QString decodeString(const QByteArray &input, QTextCodec *codec) { // First, we check if it's utf8. It is very improbable to encounter a string that looks like // valid utf8, but in fact is not. This means that if the input string passes as valid utf8, it // is safe to assume that it is. - Q_ASSERT(sizeof(const char) == sizeof(quint8)); // just to make sure + // Q_ASSERT(sizeof(const char) == sizeof(quint8)); // In God we trust... bool isUtf8 = true; int cnt = 0; for(int i = 0; i < input.size(); i++) { @@ -69,14 +69,12 @@ QString decodeString(QByteArray input, QString encoding) { //qDebug() << "Detected utf8:" << s; return s; } - QTextCodec *codec = QTextCodec::codecForName(encoding.toAscii()); - if(!codec) { - qWarning() << QString("Invalid encoding: %1").arg(encoding); - return QString::fromAscii(input); - } + //QTextCodec *codec = QTextCodec::codecForName(encoding.toAscii()); + if(!codec) return QString::fromAscii(input); return codec->toUnicode(input); } +/* not needed anymore void writeDataToDevice(QIODevice *dev, const QVariant &item) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); @@ -99,7 +97,7 @@ bool readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item) { in >> item; return true; } - +*/ uint editingDistance(const QString &s1, const QString &s2) { uint n = s1.size()+1; diff --git a/src/common/util.h b/src/common/util.h index 342356fa..ba47adfa 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -26,34 +26,26 @@ #include #include + +// TODO Use versions from NetworkInfo instead QString nickFromMask(QString mask); QString userFromMask(QString mask); QString hostFromMask(QString mask); +bool isChannelName(QString str); + //! Take a string and decode it using the specified text codec, recognizing utf8. /** This function takes a string and first checks if it is encoded in utf8, in which case it is * decoded appropriately. Otherwise, the specified text codec is used to transform the string. * \param input The input string containing encoded data - * \param encoding The text encoding we assume if it's not utf8 + * \param encoding The text codec we use if the input is not utf8 * \return The decoded string. */ -QString decodeString(QByteArray input, QString encoding = "ISO-8859-15"); - -bool isChannelName(QString str); - -/** - * Writes a QVariant to a device. The data item is prefixed with the resulting blocksize, - * so the corresponding function readDataFromDevice() can check if enough data is available - * at the device to reread the item. - */ -void writeDataToDevice(QIODevice *, const QVariant &); - -/** Reads a data item from a device that has previously been written by writeDataToDevice(). - * If not enough data bytes are available, the function returns false and the QVariant reference - * remains untouched. - */ -bool readDataFromDevice(QIODevice *, quint32 &, QVariant &); +QString decodeString(const QByteArray &input, QTextCodec *codec = 0); +// NOTE: We have static copies of these in SignalProxy... +//void writeDataToDevice(QIODevice *, const QVariant &); +//bool readDataFromDevice(QIODevice *, quint32 &, QVariant &); uint editingDistance(const QString &s1, const QString &s2); diff --git a/src/core/core.cpp b/src/core/core.cpp index ee93bcd8..d03ca6b6 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -21,8 +21,8 @@ #include "core.h" #include "coresession.h" #include "coresettings.h" +#include "signalproxy.h" #include "sqlitestorage.h" -#include "util.h" #include #include @@ -176,7 +176,7 @@ void Core::clientHasData() { Q_ASSERT(socket && blockSizes.contains(socket)); quint32 bsize = blockSizes.value(socket); QVariant item; - if(readDataFromDevice(socket, bsize, item)) { + if(SignalProxy::readDataFromDevice(socket, bsize, item)) { // we need to auth the client try { QVariantMap msg = item.toMap(); @@ -238,7 +238,7 @@ void Core::processClientInit(QTcpSocket *socket, const QVariantMap &msg) { disconnect(socket, 0, this, 0); sessions[uid]->addClient(socket); qDebug() << "Client initialized successfully."; - writeDataToDevice(socket, reply); + SignalProxy::writeDataToDevice(socket, reply); } void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) { @@ -261,7 +261,7 @@ void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) { QVariantMap reply; reply["StartWizard"] = true; reply["StorageProviders"] = availableStorageProviders(); - writeDataToDevice(socket, reply); + SignalProxy::writeDataToDevice(socket, reply); } else { // write coresettings CoreSettings s; @@ -277,7 +277,7 @@ void Core::processCoreSetup(QTcpSocket *socket, QVariantMap &msg) { QVariantMap reply; reply["StartWizard"] = true; reply["StorageProviders"] = availableStorageProviders(); - writeDataToDevice(socket, reply); + SignalProxy::writeDataToDevice(socket, reply); } } diff --git a/src/core/ctcphandler.h b/src/core/ctcphandler.h index b0d2eedf..35cab47d 100644 --- a/src/core/ctcphandler.h +++ b/src/core/ctcphandler.h @@ -34,7 +34,7 @@ public: enum CtcpType {CtcpQuery, CtcpReply}; - QStringList parse(CtcpType, QString, QString, QString); + QStringList parse(CtcpType, QString prefix, QString target, QString message); QString dequote(QString); QString XdelimDequote(QString); @@ -42,7 +42,7 @@ public: QString pack(QString ctcpTag, QString message); void query(QString bufname, QString ctcpTag, QString message); void reply(QString bufname, QString ctcpTag, QString message); - + public slots: void handleAction(CtcpType, QString prefix, QString target, QString param); void handlePing(CtcpType, QString prefix, QString target, QString param); @@ -53,7 +53,7 @@ public slots: private: QString XDELIM; QHash ctcpMDequoteHash; - QHash ctcpXDelimDequoteHash; + QHash ctcpXDelimDequoteHash; }; diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index acac5a22..ceb64980 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -31,68 +31,99 @@ #include IrcServerHandler::IrcServerHandler(Server *parent) - : BasicHandler(parent) { + : BasicHandler(parent), server(parent) { } IrcServerHandler::~IrcServerHandler() { + +} + +QString IrcServerHandler::serverDecode(const QByteArray &string) { + return server->serverDecode(string); +} + +QStringList IrcServerHandler::serverDecode(const QList &stringlist) { + QStringList list; + foreach(QByteArray s, stringlist) list << server->serverDecode(s); + return list; +} + +QString IrcServerHandler::bufferDecode(const QString &bufferName, const QByteArray &string) { + return server->bufferDecode(bufferName, string); +} + +QStringList IrcServerHandler::bufferDecode(const QString &bufferName, const QList &stringlist) { + QStringList list; + foreach(QByteArray s, stringlist) list << server->bufferDecode(bufferName, s); + return list; +} + +QString IrcServerHandler::userDecode(const QString &userNick, const QByteArray &string) { + return server->userDecode(userNick, string); +} + +QStringList IrcServerHandler::userDecode(const QString &userNick, const QList &stringlist) { + QStringList list; + foreach(QByteArray s, stringlist) list << server->userDecode(userNick, s); + return list; } /*! Handle a raw message string sent by the server. We try to find a suitable handler, otherwise we call a default handler. */ -void IrcServerHandler::handleServerMsg(QByteArray rawmsg) { +void IrcServerHandler::handleServerMsg(QByteArray msg) { try { - if(rawmsg.isEmpty()) { + if(msg.isEmpty()) { qWarning() << "Received empty string from server!"; return; } - // TODO Implement encoding conversion - /* At this point, we have a raw message as a byte array. This needs to be converted to a QString somewhere. - * Problem is, that at this point we don't know which encoding to use for the various parts of the message. - * This is something the command handler needs to take care of (e.g. PRIVMSG needs to first parse for CTCP, - * and then convert the raw strings into the correct encoding. - * We _can_ safely assume Server encoding for prefix and cmd, but not for the params. Therefore, we need to - * change from a QStringList to a QList in all the handlers, and have the handlers call decodeString - * where needed... - */ - QString msg = QString::fromLatin1(rawmsg); - - // OK, first we split the raw message into its various parts... + + // Now we split the raw message into its various parts... QString prefix = ""; + QByteArray trailing; QString cmd; - QStringList params; - - // a colon as the first chars indicates the existance of a prefix - if(msg[0] == ':') { - msg.remove(0,1); - prefix = msg.section(' ', 0, 0); - msg = msg.section(' ', 1); - } - // next string without a whitespace is the command - cmd = msg.section(' ', 0, 0).toUpper(); - msg = msg.mid(cmd.length()); - - // get the parameters - QString trailing = ""; - if(msg.contains(" :")) { - trailing = msg.section(" :", 1); - msg = msg.section(" :", 0, 0); + // First, check for a trailing parameter introduced by " :", since this might screw up splitting the msg + // NOTE: This assumes that this is true in raw encoding, but well, hopefully there are no servers running in japanese on protocol level... + int idx = msg.indexOf(" :"); + if(idx >= 0) { + if(msg.length() > idx + 2) trailing = msg.mid(idx + 2); + msg = msg.left(idx); } - if(!msg.isEmpty()) { - params << msg.split(' ', QString::SkipEmptyParts); + // OK, now it is safe to split... + QList params = msg.split(' '); + if(!trailing.isEmpty()) params << trailing; + if(params.count() < 1) { + qWarning() << "Received invalid string from server!"; + return; } - if(!trailing.isEmpty()) { - params << trailing; + + QString foo = serverDecode(params.takeFirst()); + + // a colon as the first chars indicates the existence of a prefix + if(foo[0] == ':') { + foo.remove(0, 1); + prefix = foo; + if(params.count() < 1) { + qWarning() << "Received invalid string from server!"; + return; + } + foo = serverDecode(params.takeFirst()); } + // next string without a whitespace is the command + cmd = foo.trimmed().toUpper(); + // numeric replies have the target as first param (RFC 2812 - 2.4). this is usually our own nick. Remove this! uint num = cmd.toUInt(); if(num > 0) { - Q_ASSERT(params.count() > 0); // Violation to RFC + if(params.count() == 0) { + qWarning() << "Message received from server violates RFC and is ignored!"; + return; + } params.removeFirst(); } // Now we try to find a handler for this message. BTW, I do love the Trolltech guys ;-) - handle(cmd, Q_ARG(QString, prefix), Q_ARG(QStringList, params)); + handle(cmd, Q_ARG(QString, prefix), Q_ARG(QList, params)); //handle(cmd, Q_ARG(QString, prefix)); } catch(Exception e) { emit displayMsg(Message::Error, "", e.msg()); @@ -100,7 +131,10 @@ void IrcServerHandler::handleServerMsg(QByteArray rawmsg) { } -void IrcServerHandler::defaultHandler(QString cmd, QString prefix, QStringList params) { +void IrcServerHandler::defaultHandler(QString cmd, QString prefix, QList rawparams) { + // we assume that all this happens in server encoding + QStringList params; + foreach(QByteArray r, rawparams) params << serverDecode(r); uint num = cmd.toUInt(); if(num) { // A lot of server messages don't really need their own handler because they don't do much. @@ -148,33 +182,33 @@ void IrcServerHandler::defaultHandler(QString cmd, QString prefix, QStringList p //******************************/ // IRC SERVER HANDLER //******************************/ -void IrcServerHandler::handleJoin(QString prefix, QStringList params) { +void IrcServerHandler::handleJoin(QString prefix, QList params) { Q_ASSERT(params.count() == 1); - QString channel = params[0]; + QString channel = serverDecode(params[0]); IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); emit displayMsg(Message::Join, channel, channel, prefix); //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params; ircuser->joinChannel(channel); } -void IrcServerHandler::handleKick(QString prefix, QStringList params) { +void IrcServerHandler::handleKick(QString prefix, QList params) { networkInfo()->updateNickFromMask(prefix); - IrcUser *victim = networkInfo()->ircUser(params[1]); - QString channel = params[0]; + IrcUser *victim = networkInfo()->ircUser(serverDecode(params[1])); + QString channel = serverDecode(params[0]); Q_ASSERT(victim); victim->partChannel(channel); QString msg; if(params.count() > 2) // someone got a reason! - msg = QString("%1 %2").arg(victim->nick()).arg(params[2]); + msg = QString("%1 %2").arg(victim->nick()).arg(bufferDecode(channel, params[2])); else msg = victim->nick(); - - emit displayMsg(Message::Kick, params[0], msg, prefix); + + emit displayMsg(Message::Kick, channel, msg, prefix); } -void IrcServerHandler::handleMode(QString prefix, QStringList params) { +void IrcServerHandler::handleMode(QString prefix, QList params) { Q_UNUSED(prefix) Q_UNUSED(params) @@ -213,10 +247,10 @@ void IrcServerHandler::handleMode(QString prefix, QStringList params) { // } } -void IrcServerHandler::handleNick(QString prefix, QStringList params) { +void IrcServerHandler::handleNick(QString prefix, QList params) { IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); Q_ASSERT(ircuser); - QString newnick = params[0]; + QString newnick = serverDecode(params[0]); QString oldnick = ircuser->nick(); foreach(QString channel, ircuser->channels()) { @@ -229,117 +263,108 @@ void IrcServerHandler::handleNick(QString prefix, QStringList params) { ircuser->setNick(newnick); } -void IrcServerHandler::handleNotice(QString prefix, QStringList params) { +void IrcServerHandler::handleNotice(QString prefix, QList params) { if(networkInfo()->currentServer().isEmpty() || networkInfo()->currentServer() == prefix) - emit displayMsg(Message::Server, "", params[1], prefix); + emit displayMsg(Message::Server, "", serverDecode(params[1]), prefix); else - emit displayMsg(Message::Notice, "", params[1], prefix); + emit displayMsg(Message::Notice, "", userDecode(prefix, params[1]), prefix); } -void IrcServerHandler::handlePart(QString prefix, QStringList params) { +void IrcServerHandler::handlePart(QString prefix, QList params) { IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); - QString channel = params[0]; + QString channel = serverDecode(params[0]); Q_ASSERT(ircuser); - + ircuser->partChannel(channel); - + QString msg; if(params.count() > 1) - msg = params[1]; - - emit displayMsg(Message::Part, params[0], msg, prefix); + msg = userDecode(ircuser->nick(), params[1]); + + emit displayMsg(Message::Part, channel, msg, prefix); } -void IrcServerHandler::handlePing(QString prefix, QStringList params) { - Q_UNUSED(prefix) - emit putCmd("PONG", params); +void IrcServerHandler::handlePing(QString prefix, QList params) { + Q_UNUSED(prefix); + emit putCmd("PONG", serverDecode(params)); } -void IrcServerHandler::handlePrivmsg(QString prefix, QStringList params) { - networkInfo()->updateNickFromMask(prefix); - if(params.count()<2) - params << QString(""); - - // it's possible to pack multiple privmsgs into one param using ctcp - QStringList messages = server->ctcpHandler()->parse(CtcpHandler::CtcpQuery, prefix, params[0], params[1]); - +void IrcServerHandler::handlePrivmsg(QString prefix, QList params) { + IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); + Q_ASSERT(ircuser); + if(params.count() < 2) + params << QByteArray(""); + + QString target = serverDecode(params[0]); + // are we the target or is it a channel? - if(networkInfo()->isMyNick(params[0])) { + if(networkInfo()->isMyNick(target)) { + // it's possible to pack multiple privmsgs into one param using ctcp + QStringList messages = server->ctcpHandler()->parse(CtcpHandler::CtcpQuery, prefix, target, userDecode(ircuser->nick(), params[1])); foreach(QString message, messages) { if(!message.isEmpty()) { emit displayMsg(Message::Plain, "", message, prefix, Message::PrivMsg); } } - } else { - Q_ASSERT(isChannelName(params[0])); // should be channel! + Q_ASSERT(isChannelName(target)); // should be channel! + QStringList messages = server->ctcpHandler()->parse(CtcpHandler::CtcpQuery, prefix, target, bufferDecode(target, params[1])); foreach(QString message, messages) { if(!message.isEmpty()) { - emit displayMsg(Message::Plain, params[0], message, prefix); + emit displayMsg(Message::Plain, target, message, prefix); } } } } -void IrcServerHandler::handleQuit(QString prefix, QStringList params) { +void IrcServerHandler::handleQuit(QString prefix, QList params) { IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); Q_ASSERT(ircuser); - //qDebug() << "IrcServerHandler:handleQuit" << prefix << params; QString msg; if(params.count()) - msg = params[0]; - + msg = userDecode(ircuser->nick(), params[0]); + foreach(QString channel, ircuser->channels()) emit displayMsg(Message::Quit, channel, msg, prefix); - + networkInfo()->removeIrcUser(nickFromMask(prefix)); } -void IrcServerHandler::handleTopic(QString prefix, QStringList params) { +void IrcServerHandler::handleTopic(QString prefix, QList params) { IrcUser *ircuser = networkInfo()->updateNickFromMask(prefix); - QString channel = params[0]; - QString topic = params[1]; + QString channel = serverDecode(params[0]); + QString topic = bufferDecode(channel, params[1]); Q_ASSERT(ircuser); networkInfo()->ircChannel(channel)->setTopic(topic); - emit displayMsg(Message::Server, params[0], tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel).arg(topic)); + emit displayMsg(Message::Server, channel, tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel).arg(topic)); } /* RPL_WELCOME */ -void IrcServerHandler::handle001(QString prefix, QStringList params) { +void IrcServerHandler::handle001(QString prefix, QList params) { // there should be only one param: "Welcome to the Internet Relay Network !@" - QString myhostmask = params[0].section(' ', -1, -1); + QString param = serverDecode(params[0]); + QString myhostmask = param.section(' ', -1, -1); networkInfo()->setCurrentServer(prefix); networkInfo()->setMyNick(nickFromMask(myhostmask)); - emit displayMsg(Message::Server, "", params[0], prefix); - - - // TODO: reimplement perform List! - //// send performlist - //QStringList performList = networkSettings["Perform"].toString().split( "\n" ); - //int count = performList.count(); - //for(int a = 0; a < count; a++) { - // if(!performList[a].isEmpty() ) { - // userInput(network, "", performList[a]); - // } - //} + emit displayMsg(Message::Server, "", param, prefix); } /* RPL_ISUPPORT */ // TODO Complete 005 handling, also use sensible defaults for non-sent stuff -void IrcServerHandler::handle005(QString prefix, QStringList params) { +void IrcServerHandler::handle005(QString prefix, QList params) { Q_UNUSED(prefix) - QString rpl_isupport_suffix = params.takeLast(); + QString rpl_isupport_suffix = serverDecode(params.takeLast()); if(rpl_isupport_suffix.toLower() != QString("are supported by this server")) { qWarning() << "Received invalid RPL_ISUPPORT! Suffix is:" << rpl_isupport_suffix << "Excpected: are supported by this server"; return; } - foreach(QString param, params) { + foreach(QString param, serverDecode(params)) { QString key = param.section("=", 0, 0); QString value = param.section("=", 1); networkInfo()->addSupport(key, value); @@ -348,32 +373,37 @@ void IrcServerHandler::handle005(QString prefix, QStringList params) { /* RPL_NOTOPIC */ -void IrcServerHandler::handle331(QString prefix, QStringList params) { - Q_UNUSED(prefix) - networkInfo()->ircChannel(params[0])->setTopic(QString()); - emit displayMsg(Message::Server, params[0], tr("No topic is set for %1.").arg(params[0])); +void IrcServerHandler::handle331(QString prefix, QList params) { + Q_UNUSED(prefix); + QString channel = serverDecode(params[0]); + networkInfo()->ircChannel(channel)->setTopic(QString()); + emit displayMsg(Message::Server, channel, tr("No topic is set for %1.").arg(channel)); } /* RPL_TOPIC */ -void IrcServerHandler::handle332(QString prefix, QStringList params) { - Q_UNUSED(prefix) - networkInfo()->ircChannel(params[0])->setTopic(params[1]); - emit displayMsg(Message::Server, params[0], tr("Topic for %1 is \"%2\"").arg(params[0]).arg(params[1])); +void IrcServerHandler::handle332(QString prefix, QList params) { + Q_UNUSED(prefix); + QString channel = serverDecode(params[0]); + QString topic = bufferDecode(channel, params[1]); + networkInfo()->ircChannel(channel)->setTopic(topic); + emit displayMsg(Message::Server, channel, tr("Topic for %1 is \"%2\"").arg(channel, topic)); } /* Topic set by... */ -void IrcServerHandler::handle333(QString prefix, QStringList params) { - Q_UNUSED(prefix) - emit displayMsg(Message::Server, params[0], tr("Topic set by %1 on %2").arg(params[1]).arg(QDateTime::fromTime_t(params[2].toUInt()).toString())); +void IrcServerHandler::handle333(QString prefix, QList params) { + Q_UNUSED(prefix); + QString channel = serverDecode(params[0]); + emit displayMsg(Message::Server, channel, tr("Topic set by %1 on %2") + .arg(bufferDecode(channel, params[1]), QDateTime::fromTime_t(bufferDecode(channel, params[2]).toUInt()).toString())); } /* RPL_NAMREPLY */ -void IrcServerHandler::handle353(QString prefix, QStringList params) { +void IrcServerHandler::handle353(QString prefix, QList params) { Q_UNUSED(prefix) params.removeFirst(); // either "=", "*" or "@" indicating a public, private or secret channel - QString channelname = params.takeFirst(); + QString channelname = serverDecode(params.takeFirst()); - foreach(QString nick, params.takeFirst().split(' ')) { + foreach(QString nick, serverDecode(params.takeFirst()).split(' ')) { QString mode = QString(); if(networkInfo()->prefixes().contains(nick[0])) { @@ -390,7 +420,7 @@ void IrcServerHandler::handle353(QString prefix, QStringList params) { } /* ERR_ERRONEUSNICKNAME */ -void IrcServerHandler::handle432(QString prefix, QStringList params) { +void IrcServerHandler::handle432(QString prefix, QList params) { Q_UNUSED(prefix) Q_UNUSED(params) emit displayMsg(Message::Error, "", tr("Your desired nickname contains illegal characters!")); @@ -424,9 +454,9 @@ void IrcServerHandler::handle432(QString prefix, QStringList params) { } /* ERR_NICKNAMEINUSE */ -void IrcServerHandler::handle433(QString prefix, QStringList params) { +void IrcServerHandler::handle433(QString prefix, QList params) { Q_UNUSED(prefix) - QString errnick = params[0]; + QString errnick = serverDecode(params[0]); emit displayMsg(Message::Error, "", tr("Nick %1 is already taken").arg(errnick)); emit displayMsg(Message::Error, "", tr("Please use /nick to continue your IRC-Session!")); // FIXME! diff --git a/src/core/ircserverhandler.h b/src/core/ircserverhandler.h index 62cc4b5a..8a0d3841 100644 --- a/src/core/ircserverhandler.h +++ b/src/core/ircserverhandler.h @@ -31,29 +31,40 @@ public: ~IrcServerHandler(); void handleServerMsg(QByteArray rawMsg); - + + QString serverDecode(const QByteArray &string); + QStringList serverDecode(const QList &stringlist); + QString bufferDecode(const QString &bufferName, const QByteArray &string); + QStringList bufferDecode(const QString &bufferName, const QList &stringlist); + QString userDecode(const QString &userNick, const QByteArray &string); + QStringList userDecode(const QString &userNick, const QList &stringlist); + + public slots: - void handleJoin(QString, QStringList); - void handleKick(QString, QStringList); - void handleMode(QString, QStringList); - void handleNick(QString, QStringList); - void handleNotice(QString, QStringList); - void handlePart(QString, QStringList); - void handlePing(QString, QStringList); - void handlePrivmsg(QString, QStringList); - void handleQuit(QString, QStringList); - void handleTopic(QString, QStringList); - - void handle001(QString, QStringList); // RPL_WELCOME - void handle005(QString, QStringList); // RPL_ISUPPORT - void handle331(QString, QStringList); // RPL_NOTOPIC - void handle332(QString, QStringList); // RPL_TOPIC - void handle333(QString, QStringList); // Topic set by... - void handle353(QString, QStringList); // RPL_NAMREPLY - void handle432(QString, QStringList); // ERR_ERRONEUSNICKNAME - void handle433(QString, QStringList); // ERR_NICKNAMEINUSE - - void defaultHandler(QString cmd, QString prefix, QStringList params); + void handleJoin(QString, QList); + void handleKick(QString, QList); + void handleMode(QString, QList); + void handleNick(QString, QList); + void handleNotice(QString, QList); + void handlePart(QString, QList); + void handlePing(QString, QList); + void handlePrivmsg(QString, QList); + void handleQuit(QString, QList); + void handleTopic(QString, QList); + + void handle001(QString, QList); // RPL_WELCOME + void handle005(QString, QList); // RPL_ISUPPORT + void handle331(QString, QList); // RPL_NOTOPIC + void handle332(QString, QList); // RPL_TOPIC + void handle333(QString, QList); // Topic set by... + void handle353(QString, QList); // RPL_NAMREPLY + void handle432(QString, QList); // ERR_ERRONEUSNICKNAME + void handle433(QString, QList); // ERR_NICKNAMEINUSE + + void defaultHandler(QString cmd, QString prefix, QList params); + + private: + Server *server; }; diff --git a/src/core/server.cpp b/src/core/server.cpp index f88efab4..b222b0d7 100644 --- a/src/core/server.cpp +++ b/src/core/server.cpp @@ -44,6 +44,8 @@ Server::Server(UserId uid, NetworkId networkId, QString net, const QVariant &sta _previousState(state) { connect(networkInfo(), SIGNAL(currentServerSet(const QString &)), this, SLOT(sendPerform())); + networkInfo()->setCodecForEncoding("ISO-8859-15"); // FIXME + networkInfo()->setCodecForDecoding("ISO-8859-15"); // FIXME networkInfo()->setNetworkName(net); networkInfo()->setProxy(coreSession()->signalProxy()); } @@ -65,6 +67,39 @@ void Server::run() { exec(); } +QString Server::serverDecode(const QByteArray &string) const { + return networkInfo()->decodeString(string); +} + +QString Server::bufferDecode(const QString &bufferName, const QByteArray &string) const { + Q_UNUSED(bufferName); + // TODO: Implement buffer-specific encodings + return networkInfo()->decodeString(string); +} + +QString Server::userDecode(const QString &userNick, const QByteArray &string) const { + IrcUser *user = networkInfo()->ircUser(userNick); + if(user) return user->decodeString(string); + return networkInfo()->decodeString(string); +} + +QByteArray Server::serverEncode(const QString &string) const { + return networkInfo()->encodeString(string); +} + +QByteArray Server::bufferEncode(const QString &bufferName, const QString &string) const { + Q_UNUSED(bufferName); + // TODO: Implement buffer-specific encodings + return networkInfo()->encodeString(string); +} + +QByteArray Server::userEncode(const QString &userNick, const QString &string) const { + IrcUser *user = networkInfo()->ircUser(userNick); + if(user) return user->encodeString(string); + return networkInfo()->encodeString(string); +} + + void Server::connectToIrc(QString net) { if(net != networkName()) return; // not me! diff --git a/src/core/server.h b/src/core/server.h index 32060859..17bec4f7 100644 --- a/src/core/server.h +++ b/src/core/server.h @@ -65,7 +65,25 @@ public: CtcpHandler *ctcpHandler() const { return _ctcpHandler; } QVariant state(); ///< Return data necessary to restore the server's state upon core restart - + + //! Decode a string using the server (network) decoding. + QString serverDecode(const QByteArray &string) const; + + //! Decode a string using a buffer-specific encoding if one is set (and use the server encoding else). + QString bufferDecode(const QString &bufferName, const QByteArray &string) const; + + //! Decode a string using a IrcUser specific encoding, if one exists (using the server encoding else). + QString userDecode(const QString &userNick, const QByteArray &string) const; + + //! Encode a string using the server (network) encoding. + QByteArray serverEncode(const QString &string) const; + + //! Encode a string using the buffer-specific encoding, if set, and use the server encoding else. + QByteArray bufferEncode(const QString &bufferName, const QString &string) const; + + //! Encode a string using the user-specific encoding, if set, and use the server encoding else. + QByteArray userEncode(const QString &userNick, const QString &string) const; + public slots: // void setServerOptions(); void connectToIrc(QString net); -- 2.20.1