My X-Mas present to you: partially working encodings! \o/
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 25 Dec 2007 00:36:10 +0000 (00:36 +0000)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 25 Dec 2007 00:36:10 +0000 (00:36 +0000)
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...

18 files changed:
Quassel.kdevelop.filelist
src/client/client.cpp
src/common/bufferinfo.cpp
src/common/ircchannel.cpp
src/common/ircchannel.h
src/common/ircuser.cpp
src/common/ircuser.h
src/common/networkinfo.cpp
src/common/networkinfo.h
src/common/signalproxy.h
src/common/util.cpp
src/common/util.h
src/core/core.cpp
src/core/ctcphandler.h
src/core/ircserverhandler.cpp
src/core/ircserverhandler.h
src/core/server.cpp
src/core/server.h

index dfd181f..f6c3b0b 100644 (file)
@@ -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
index 28c656e..e78fc17 100644 (file)
@@ -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()) {
index 2eb790d..a68101c 100644 (file)
@@ -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) {
index 47082dd..9f1a91f 100644 (file)
 //#include "nicktreemodel.h"
 #include "signalproxy.h"
 #include "ircuser.h"
+#include "util.h"
 
 #include <QMapIterator>
 #include <QHashIterator>
+#include <QTextCodec>
 
 #include <QDebug>
 
@@ -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:
 // ====================
index 521e1a1..a499b24 100644 (file)
@@ -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<IrcUser *, QString> _userModes;
 
   NetworkInfo *networkInfo;
+
+  QTextCodec *_codecForEncoding;
+  QTextCodec *_codecForDecoding;
 };
 
 #endif
index 333ee93..91b3a2b 100644 (file)
@@ -25,6 +25,7 @@
 #include "signalproxy.h"
 #include "ircchannel.h"
 
+#include <QTextCodec>
 #include <QDebug>
 
 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:
 // ====================
index e933fb6..33dd143 100644 (file)
@@ -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<QString> &usermodes);
 //   QSet<QString> 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<QString> _channels;
   QSet<IrcChannel *> _channels;
   QString _userModes;
-  
+
   NetworkInfo *networkInfo;
+
+  QTextCodec *_codecForEncoding;
+  QTextCodec *_codecForDecoding;
 };
 
 #endif
index 89e88cc..62dacac 100644 (file)
@@ -24,6 +24,7 @@
 #include "ircchannel.h"
 
 #include <QDebug>
+#include <QTextCodec>
 
 #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<IrcChannel *> 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:
 // ====================
index 0c6516b..1f712fb 100644 (file)
@@ -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 &param) const;
   QString support(const QString &param) const;
-  
+
   IrcUser *newIrcUser(const QString &hostmask);
   IrcUser *ircUser(QString nickname) const;
   QList<IrcUser *> ircUsers() const;
-  
+
   IrcChannel *newIrcChannel(const QString &channelname);
   IrcChannel *ircChannel(QString channelname);
   QList<IrcChannel *> 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 &currentServer);
@@ -153,6 +163,9 @@ private:
   QPointer<SignalProxy> _proxy;
   void determinePrefixes();
 
+  QTextCodec *_codecForEncoding;
+  QTextCodec *_codecForDecoding;
+
 };
 
 #endif
index b787c2a..be2c9ff 100644 (file)
@@ -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);
index db1c6da..25ff475 100644 (file)
@@ -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;
index 342356f..ba47adf 100644 (file)
 #include <QString>
 #include <QMetaMethod>
 
+
+// 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);
 
index ee93bcd..d03ca6b 100644 (file)
@@ -21,8 +21,8 @@
 #include "core.h"
 #include "coresession.h"
 #include "coresettings.h"
+#include "signalproxy.h"
 #include "sqlitestorage.h"
-#include "util.h"
 
 #include <QMetaObject>
 #include <QMetaMethod>
@@ -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);
   }
 }
 
index b0d2eed..35cab47 100644 (file)
@@ -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<QString, QString> ctcpMDequoteHash;
-  QHash<QString, QString> ctcpXDelimDequoteHash;    
+  QHash<QString, QString> ctcpXDelimDequoteHash;
 
 
 };
index acac5a2..ceb6498 100644 (file)
 #include <QDebug>
 
 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<QByteArray> &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<QByteArray> &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<QByteArray> &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<QByteArray> 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<QByteArray> 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<QByteArray>, 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> params) {
   // there should be only one param: "Welcome to the Internet Relay Network <nick>!<user>@<host>"
-  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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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<QByteArray> 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 <othernick> to continue your IRC-Session!"));
   // FIXME!
index 62cc4b5..8a0d384 100644 (file)
@@ -31,29 +31,40 @@ public:
   ~IrcServerHandler();
 
   void handleServerMsg(QByteArray rawMsg);
-  
+
+  QString serverDecode(const QByteArray &string);
+  QStringList serverDecode(const QList<QByteArray> &stringlist);
+  QString bufferDecode(const QString &bufferName, const QByteArray &string);
+  QStringList bufferDecode(const QString &bufferName, const QList<QByteArray> &stringlist);
+  QString userDecode(const QString &userNick, const QByteArray &string);
+  QStringList userDecode(const QString &userNick, const QList<QByteArray> &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<QByteArray>);
+  void handleKick(QString, QList<QByteArray>);
+  void handleMode(QString, QList<QByteArray>);
+  void handleNick(QString, QList<QByteArray>);
+  void handleNotice(QString, QList<QByteArray>);
+  void handlePart(QString, QList<QByteArray>);
+  void handlePing(QString, QList<QByteArray>);
+  void handlePrivmsg(QString, QList<QByteArray>);
+  void handleQuit(QString, QList<QByteArray>);
+  void handleTopic(QString, QList<QByteArray>);
+
+  void handle001(QString, QList<QByteArray>);   // RPL_WELCOME
+  void handle005(QString, QList<QByteArray>);   // RPL_ISUPPORT
+  void handle331(QString, QList<QByteArray>);   // RPL_NOTOPIC
+  void handle332(QString, QList<QByteArray>);   // RPL_TOPIC
+  void handle333(QString, QList<QByteArray>);   // Topic set by...
+  void handle353(QString, QList<QByteArray>);   // RPL_NAMREPLY
+  void handle432(QString, QList<QByteArray>);   // ERR_ERRONEUSNICKNAME
+  void handle433(QString, QList<QByteArray>);   // ERR_NICKNAMEINUSE
+
+  void defaultHandler(QString cmd, QString prefix, QList<QByteArray> params);
+
+  private:
+    Server *server;
 };
 
 
index f88efab..b222b0d 100644 (file)
@@ -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!
index 3206085..17bec4f 100644 (file)
@@ -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);