From: Marcus Eggenberger Date: Sun, 7 Dec 2008 18:06:24 +0000 (+0100) Subject: Merging NetworkConnection into CoreNetwork. X-Git-Tag: 0.4.0~343 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=a5dfcc8ecf8b81025d24b3c5c816169e3e030ea4;hp=f7379184b7c0ae4e53d7470809f84e2ad3239ec1 Merging NetworkConnection into CoreNetwork. This is a major internal change. Needs thorough testing. --- diff --git a/src/common/ircchannel.cpp b/src/common/ircchannel.cpp index 04ff1435..387a2702 100644 --- a/src/common/ircchannel.cpp +++ b/src/common/ircchannel.cpp @@ -192,8 +192,9 @@ void IrcChannel::part(IrcUser *ircuser) { disconnect(ircuser, 0, this, 0); emit ircUserParted(ircuser); - if(network->isMe(ircuser)) { - // we left -> clean up the channel and destroy it + if(network->isMe(ircuser) || _userModes.isEmpty()) { + // in either case we're no longer in the channel + // -> clean up the channel and destroy it QList users = _userModes.keys(); _userModes.clear(); foreach(IrcUser *user, users) { diff --git a/src/common/ircuser.cpp b/src/common/ircuser.cpp index ab773d9e..f8a1e0a9 100644 --- a/src/common/ircuser.cpp +++ b/src/common/ircuser.cpp @@ -218,7 +218,6 @@ void IrcUser::joinChannel(IrcChannel *channel) { if(!_channels.contains(channel)) { _channels.insert(channel); channel->joinIrcUsers(this); - // connect(channel, SIGNAL(destroyed()), this, SLOT(channelDestroyed())); } } diff --git a/src/common/network.cpp b/src/common/network.cpp index 9c0f915b..df2a8376 100644 --- a/src/common/network.cpp +++ b/src/common/network.cpp @@ -27,6 +27,21 @@ QTextCodec *Network::_defaultCodecForServer = 0; QTextCodec *Network::_defaultCodecForEncoding = 0; QTextCodec *Network::_defaultCodecForDecoding = 0; + +Network::Server Network::Server::fromVariant(const QVariant &variant) { + QVariantMap serverMap = variant.toMap(); + return Server(serverMap["Host"].toString(), serverMap["Port"].toUInt(), serverMap["Password"].toString(), serverMap["UseSSL"].toBool()); +} + +QVariant Network::Server::toVariant() const { + QVariantMap serverMap; + serverMap["Host"] = host; + serverMap["Port"] = port; + serverMap["Password"] = password; + serverMap["UseSSL"] = useSsl; + return QVariant::fromValue(serverMap); +} + // ==================== // Public: // ==================== @@ -79,7 +94,7 @@ NetworkInfo Network::networkInfo() const { info.codecForServer = codecForServer(); info.codecForEncoding = codecForEncoding(); info.codecForDecoding = codecForDecoding(); - info.serverList = serverList(); + info.serverList = variantServerList(); info.useRandomServer = useRandomServer(); info.perform = perform(); info.useAutoIdentify = useAutoIdentify(); @@ -137,6 +152,16 @@ QStringList Network::nicks() const { return nicks; } +QVariantList Network::variantServerList() const { + QVariantList servers; + ServerList::const_iterator serverIter = _serverList.constBegin(); + while(serverIter != _serverList.constEnd()) { + servers << serverIter->toVariant(); + serverIter++; + } + return servers; +} + QString Network::prefixes() { if(_prefixes.isNull()) determinePrefixes(); @@ -240,14 +265,21 @@ void Network::removeChansAndUsers() { _ircUsers.clear(); QList channels = ircChannels(); _ircChannels.clear(); - + foreach(IrcChannel *channel, channels) { + proxy()->detachObject(channel); disconnect(channel, 0, this, 0); } foreach(IrcUser *user, users) { + proxy()->detachObject(user); disconnect(user, 0, this, 0); + } + + // the second loop is needed because quit can have sideffects + foreach(IrcUser *user, users) { user->quit(); } + qDeleteAll(users); qDeleteAll(channels); } @@ -445,7 +477,10 @@ void Network::setIdentity(IdentityId id) { } void Network::setServerList(const QVariantList &serverList) { - _serverList = serverList; + _serverList.clear(); + foreach(QVariant variant, serverList) { + _serverList << Server::fromVariant(variant); + } emit serverListSet(serverList); } @@ -569,7 +604,6 @@ void Network::initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels) { newIrcChannel(channelIter.key(), channelIter.value().toMap()); channelIter++; } - } void Network::initSetSupports(const QVariantMap &supports) { @@ -580,6 +614,12 @@ void Network::initSetSupports(const QVariantMap &supports) { } } +void Network::initSetServerList(const QVariantList &serverList) { + foreach(QVariant variant, serverList) { + _serverList << Server::fromVariant(variant); + } +} + IrcUser *Network::updateNickFromMask(const QString &mask) { QString nick(nickFromMask(mask).toLower()); IrcUser *ircuser; diff --git a/src/common/network.h b/src/common/network.h index 4a432d84..84e86998 100644 --- a/src/common/network.h +++ b/src/common/network.h @@ -88,6 +88,19 @@ public: D_CHANMODE = 0x08 }; + struct Server { + QString host; + uint port; + QString password; + bool useSsl; + Server() : port(0), useSsl(false) {} + Server(const QString &host, uint port, const QString &password, bool useSsl) + : host(host), port(port), password(password), useSsl(useSsl) {} + + static Server fromVariant(const QVariant &variant); + QVariant toVariant() const; + }; + typedef QList ServerList; Network(const NetworkId &networkid, QObject *parent = 0); ~Network(); @@ -122,7 +135,8 @@ public: inline IdentityId identity() const { return _identity; } QStringList nicks() const; inline QStringList channels() const { return _ircChannels.keys(); } - inline const QVariantList &serverList() const { return _serverList; } + inline const ServerList &serverList() const { return _serverList; } + QVariantList variantServerList() const; inline bool useRandomServer() const { return _useRandomServer; } inline const QStringList &perform() const { return _perform; } inline bool useAutoIdentify() const { return _useAutoIdentify; } @@ -176,16 +190,15 @@ public: static void setDefaultCodecForEncoding(const QByteArray &name); static void setDefaultCodecForDecoding(const QByteArray &name); - bool autoAwayActive() const { return _autoAwayActive; } - void setAutoAwayActive(bool active) { _autoAwayActive = active; } + inline bool autoAwayActive() const { return _autoAwayActive; } + inline void setAutoAwayActive(bool active) { _autoAwayActive = active; } public slots: void setNetworkName(const QString &networkName); void setCurrentServer(const QString ¤tServer); void setConnected(bool isConnected); - //void setConnectionState(Network::ConnectionState state); void setConnectionState(int state); - void setMyNick(const QString &mynick); + virtual void setMyNick(const QString &mynick); void setLatency(int latency); void setIdentity(IdentityId); @@ -195,9 +208,9 @@ public slots: void setUseAutoIdentify(bool); void setAutoIdentifyService(const QString &); void setAutoIdentifyPassword(const QString &); - void setUseAutoReconnect(bool); - void setAutoReconnectInterval(quint32); - void setAutoReconnectRetries(quint16); + virtual void setUseAutoReconnect(bool); + virtual void setAutoReconnectInterval(quint32); + virtual void setAutoReconnectRetries(quint16); void setUnlimitedReconnectRetries(bool); void setRejoinChannels(bool); @@ -213,17 +226,13 @@ public slots: //init geters QVariantMap initSupports() const; - inline QVariantList initServerList() const { return serverList(); } + inline QVariantList initServerList() const { return variantServerList(); } virtual QVariantMap initIrcUsersAndChannels() const; -// QStringList initIrcUsers() const; -// QStringList initIrcChannels() const; //init seters void initSetSupports(const QVariantMap &supports); - inline void initSetServerList(const QVariantList &serverList) { setServerList(serverList); } + void initSetServerList(const QVariantList &serverList); virtual void initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels); -// void initSetIrcUsers(const QStringList &hostmasks); -// void initSetIrcChannels(const QStringList &channels); IrcUser *updateNickFromMask(const QString &mask); @@ -278,8 +287,8 @@ signals: void ircChannelAdded(const QString &channelname); void ircChannelAdded(IrcChannel *); - void connectRequested(NetworkId id = 0) const; - void disconnectRequested(NetworkId id = 0) const; + void connectRequested() const; + void disconnectRequested() const; void setNetworkInfoRequested(const NetworkInfo &) const; protected: @@ -305,7 +314,7 @@ private: QHash _ircChannels; // stores all known channels QHash _supports; // stores results from RPL_ISUPPORT - QVariantList _serverList; + ServerList _serverList; bool _useRandomServer; QStringList _perform; diff --git a/src/common/signalproxy.cpp b/src/common/signalproxy.cpp index 5d302030..c551c246 100644 --- a/src/common/signalproxy.cpp +++ b/src/common/signalproxy.cpp @@ -440,8 +440,6 @@ void SignalProxy::setArgTypes(QObject* obj, int methodId) { const QList &SignalProxy::argTypes(QObject *obj, int methodId) { const QMetaObject *meta = metaObject(obj); - if(!_classInfo.contains(meta)) - qDebug() << obj << meta; Q_ASSERT(_classInfo.contains(meta)); if(!_classInfo[meta]->argTypes.contains(methodId)) setArgTypes(obj, methodId); @@ -719,9 +717,9 @@ void SignalProxy::detachSender() { } void SignalProxy::detachObject(QObject* obj) { + stopSync(static_cast(obj)); detachSignals(obj); detachSlots(obj); - stopSync(static_cast(obj)); } void SignalProxy::detachSignals(QObject* sender) { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4491f568..77525fb4 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -24,7 +24,6 @@ set(SOURCES coreusersettings.cpp ctcphandler.cpp ircserverhandler.cpp - networkconnection.cpp sessionthread.cpp sqlitestorage.cpp storage.cpp @@ -46,7 +45,6 @@ set(MOC_HDRS coresession.h ctcphandler.h ircserverhandler.h - networkconnection.h sqlitestorage.h storage.h sessionthread.h diff --git a/src/core/basichandler.cpp b/src/core/basichandler.cpp index 0bb1438b..4df9ea25 100644 --- a/src/core/basichandler.cpp +++ b/src/core/basichandler.cpp @@ -24,20 +24,20 @@ #include "util.h" #include "logger.h" -BasicHandler::BasicHandler(NetworkConnection *parent) +BasicHandler::BasicHandler(CoreNetwork *parent) : QObject(parent), defaultHandler(-1), - _networkConnection(parent), + _network(parent), initDone(false) { connect(this, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)), - networkConnection(), SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags))); + network(), SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags))); connect(this, SIGNAL(putCmd(QString, const QList &, const QByteArray &)), - networkConnection(), SLOT(putCmd(QString, const QList &, const QByteArray &))); + network(), SLOT(putCmd(QString, const QList &, const QByteArray &))); connect(this, SIGNAL(putRawLine(const QByteArray &)), - networkConnection(), SLOT(putRawLine(const QByteArray &))); + network(), SLOT(putRawLine(const QByteArray &))); } QStringList BasicHandler::providesHandlers() { @@ -94,64 +94,64 @@ void BasicHandler::handle(const QString &member, QGenericArgument val0, } QString BasicHandler::serverDecode(const QByteArray &string) { - return networkConnection()->serverDecode(string); + return network()->serverDecode(string); } QStringList BasicHandler::serverDecode(const QList &stringlist) { QStringList list; - foreach(QByteArray s, stringlist) list << networkConnection()->serverDecode(s); + foreach(QByteArray s, stringlist) list << network()->serverDecode(s); return list; } QString BasicHandler::channelDecode(const QString &bufferName, const QByteArray &string) { - return networkConnection()->channelDecode(bufferName, string); + return network()->channelDecode(bufferName, string); } QStringList BasicHandler::channelDecode(const QString &bufferName, const QList &stringlist) { QStringList list; - foreach(QByteArray s, stringlist) list << networkConnection()->channelDecode(bufferName, s); + foreach(QByteArray s, stringlist) list << network()->channelDecode(bufferName, s); return list; } QString BasicHandler::userDecode(const QString &userNick, const QByteArray &string) { - return networkConnection()->userDecode(userNick, string); + return network()->userDecode(userNick, string); } QStringList BasicHandler::userDecode(const QString &userNick, const QList &stringlist) { QStringList list; - foreach(QByteArray s, stringlist) list << networkConnection()->userDecode(userNick, s); + foreach(QByteArray s, stringlist) list << network()->userDecode(userNick, s); return list; } /*** ***/ QByteArray BasicHandler::serverEncode(const QString &string) { - return networkConnection()->serverEncode(string); + return network()->serverEncode(string); } QList BasicHandler::serverEncode(const QStringList &stringlist) { QList list; - foreach(QString s, stringlist) list << networkConnection()->serverEncode(s); + foreach(QString s, stringlist) list << network()->serverEncode(s); return list; } QByteArray BasicHandler::channelEncode(const QString &bufferName, const QString &string) { - return networkConnection()->channelEncode(bufferName, string); + return network()->channelEncode(bufferName, string); } QList BasicHandler::channelEncode(const QString &bufferName, const QStringList &stringlist) { QList list; - foreach(QString s, stringlist) list << networkConnection()->channelEncode(bufferName, s); + foreach(QString s, stringlist) list << network()->channelEncode(bufferName, s); return list; } QByteArray BasicHandler::userEncode(const QString &userNick, const QString &string) { - return networkConnection()->userEncode(userNick, string); + return network()->userEncode(userNick, string); } QList BasicHandler::userEncode(const QString &userNick, const QStringList &stringlist) { QList list; - foreach(QString s, stringlist) list << networkConnection()->userEncode(userNick, s); + foreach(QString s, stringlist) list << network()->userEncode(userNick, s); return list; } diff --git a/src/core/basichandler.h b/src/core/basichandler.h index c276c96c..0df18306 100644 --- a/src/core/basichandler.h +++ b/src/core/basichandler.h @@ -18,8 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#ifndef _BASICHANDLER_H_ -#define _BASICHANDLER_H_ +#ifndef BASICHANDLER_H_ +#define BASICHANDLER_H_ #include #include @@ -29,7 +29,7 @@ #include "message.h" -#include "networkconnection.h" +#include "corenetwork.h" class CoreSession; @@ -37,7 +37,7 @@ class BasicHandler : public QObject { Q_OBJECT public: - BasicHandler(NetworkConnection *parent = 0); + BasicHandler(CoreNetwork *parent = 0); QStringList providesHandlers(); @@ -71,9 +71,8 @@ protected: QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument()); - inline Network *network() const { return _networkConnection->network(); } - inline NetworkConnection *networkConnection() const { return _networkConnection; } - inline CoreSession *coreSession() const { return _networkConnection->coreSession(); } + inline CoreNetwork *network() const { return _network; } + inline CoreSession *coreSession() const { return _network->coreSession(); } BufferInfo::Type typeByTarget(const QString &target) const; @@ -81,7 +80,7 @@ private: const QHash &handlerHash(); QHash _handlerHash; int defaultHandler; - NetworkConnection *_networkConnection; + CoreNetwork *_network; bool initDone; }; #endif diff --git a/src/core/coreirclisthelper.cpp b/src/core/coreirclisthelper.cpp index dbcdcc4c..312ed3de 100644 --- a/src/core/coreirclisthelper.cpp +++ b/src/core/coreirclisthelper.cpp @@ -20,7 +20,7 @@ #include "coreirclisthelper.h" -#include "networkconnection.h" +#include "corenetwork.h" #include "userinputhandler.h" QVariantList CoreIrcListHelper::requestChannelList(const NetworkId &netId, const QStringList &channelFilters) { @@ -44,10 +44,10 @@ bool CoreIrcListHelper::addChannel(const NetworkId &netId, const QString &channe } bool CoreIrcListHelper::dispatchQuery(const NetworkId &netId, const QString &query) { - NetworkConnection *networkConnection = coreSession()->networkConnection(netId); - if(networkConnection) { + CoreNetwork *network = coreSession()->network(netId); + if(network) { _channelLists[netId] = QList(); - networkConnection->userInputHandler()->handleList(BufferInfo(), query); + network->userInputHandler()->handleList(BufferInfo(), query); _queryTimeout[startTimer(10000)] = netId; return true; } else { diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index a7cbd224..a5c40e62 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -18,16 +18,481 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "core.h" #include "corenetwork.h" + + +#include "core.h" #include "coresession.h" +#include "identity.h" -#include "logger.h" +#include "ircserverhandler.h" +#include "userinputhandler.h" +#include "ctcphandler.h" CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session) : Network(networkid, session), - _coreSession(session) + _coreSession(session), + _ircServerHandler(new IrcServerHandler(this)), + _userInputHandler(new UserInputHandler(this)), + _ctcpHandler(new CtcpHandler(this)), + _autoReconnectCount(0), + _quitRequested(false), + + _previousConnectionAttemptFailed(false), + _lastUsedServerIndex(0), + + // TODO make autowho configurable (possibly per-network) + _autoWhoEnabled(true), + _autoWhoInterval(90), + _autoWhoNickLimit(0), // unlimited + _autoWhoDelay(3) { + _autoReconnectTimer.setSingleShot(true); + _socketCloseTimer.setSingleShot(true); + connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); + + _pingTimer.setInterval(60000); + connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); + + _autoWhoTimer.setInterval(_autoWhoDelay * 1000); + _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); + + QHash channels = coreSession()->persistentChannels(networkId()); + foreach(QString chan, channels.keys()) { + _channelKeys[chan.toLower()] = channels[chan]; + } + + connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect())); + connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho())); + connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle())); + connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue())); + connect(this, SIGNAL(connectRequested()), this, SLOT(connectToIrc())); + + + connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData())); +#ifdef HAVE_SSL + connect(&socket, SIGNAL(connected()), this, SLOT(sslSocketConnected())); + connect(&socket, SIGNAL(encrypted()), this, SLOT(socketInitialized())); + connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); +#else + connect(&socket, SIGNAL(connected()), this, SLOT(socketInitialized())); +#endif +} + +CoreNetwork::~CoreNetwork() { + if(connectionState() != Disconnected && connectionState() != Network::Reconnecting) + disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! + disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up + delete _ircServerHandler; + delete _userInputHandler; + delete _ctcpHandler; +} + +QString CoreNetwork::channelDecode(const QString &bufferName, const QByteArray &string) const { + if(!bufferName.isEmpty()) { + IrcChannel *channel = ircChannel(bufferName); + if(channel) + return channel->decodeString(string); + } + return decodeString(string); +} + +QString CoreNetwork::userDecode(const QString &userNick, const QByteArray &string) const { + IrcUser *user = ircUser(userNick); + if(user) + return user->decodeString(string); + return decodeString(string); +} + +QByteArray CoreNetwork::channelEncode(const QString &bufferName, const QString &string) const { + if(!bufferName.isEmpty()) { + IrcChannel *channel = ircChannel(bufferName); + if(channel) + return channel->encodeString(string); + } + return encodeString(string); +} + +QByteArray CoreNetwork::userEncode(const QString &userNick, const QString &string) const { + IrcUser *user = ircUser(userNick); + if(user) + return user->encodeString(string); + return encodeString(string); +} + +void CoreNetwork::connectToIrc(bool reconnecting) { + if(!reconnecting && useAutoReconnect() && _autoReconnectCount == 0) { + _autoReconnectTimer.setInterval(autoReconnectInterval() * 1000); + if(unlimitedReconnectRetries()) + _autoReconnectCount = -1; + else + _autoReconnectCount = autoReconnectRetries(); + } + if(serverList().isEmpty()) { + qWarning() << "Server list empty, ignoring connect request!"; + return; + } + Identity *identity = identityPtr(); + if(!identity) { + qWarning() << "Invalid identity configures, ignoring connect request!"; + return; + } + // use a random server? + if(useRandomServer()) { + _lastUsedServerIndex = qrand() % serverList().size(); + } else if(_previousConnectionAttemptFailed) { + // cycle to next server if previous connection attempt failed + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Connection failed. Cycling to next Server")); + if(++_lastUsedServerIndex == serverList().size()) { + _lastUsedServerIndex = 0; + } + } + _previousConnectionAttemptFailed = false; + + Server server = usedServer(); + displayStatusMsg(tr("Connecting to %1:%2...").arg(server.host).arg(server.port)); + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Connecting to %1:%2...").arg(server.host).arg(server.port)); + socket.connectToHost(server.host, server.port); +} + +void CoreNetwork::disconnectFromIrc(bool requested, const QString &reason) { + _quitRequested = requested; // see socketDisconnected(); + _autoReconnectTimer.stop(); + _autoReconnectCount = 0; // prohibiting auto reconnect + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting.")); + if(socket.state() == QAbstractSocket::UnconnectedState) { + socketDisconnected(); + } else if(socket.state() < QAbstractSocket::ConnectedState || !requested) { + // we might be in a state waiting for a timeout... + // or (!requested) this is a core shutdown... + // in both cases we don't really care... set a disconnected state + socket.close(); + socketDisconnected(); + } else { + // quit gracefully if it's user requested quit + userInputHandler()->issueQuit(reason); + // the irc server has 10 seconds to close the socket + _socketCloseTimer.start(10000); + } +} + +void CoreNetwork::userInput(BufferInfo buf, QString msg) { + userInputHandler()->handleUserInput(buf, msg); +} + +void CoreNetwork::putRawLine(QByteArray s) { + if(_tokenBucket > 0) + writeToSocket(s); + else + _msgQueue.append(s); +} + +void CoreNetwork::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix) { + QByteArray msg; + + if(!prefix.isEmpty()) + msg += ":" + prefix + " "; + msg += cmd.toUpper().toAscii(); + + for(int i = 0; i < params.size() - 1; i++) { + msg += " " + params[i]; + } + if(!params.isEmpty()) + msg += " :" + params.last(); + + putRawLine(msg); +} + +void CoreNetwork::setChannelJoined(const QString &channel) { + _autoWhoQueue.prepend(channel.toLower()); // prepend so this new chan is the first to be checked + + Core::setChannelPersistent(userId(), networkId(), channel, true); + Core::setPersistentChannelKey(userId(), networkId(), channel, _channelKeys[channel.toLower()]); +} + +void CoreNetwork::setChannelParted(const QString &channel) { + removeChannelKey(channel); + _autoWhoQueue.removeAll(channel.toLower()); + _autoWhoInProgress.remove(channel.toLower()); + + Core::setChannelPersistent(userId(), networkId(), channel, false); +} + +void CoreNetwork::addChannelKey(const QString &channel, const QString &key) { + if(key.isEmpty()) { + removeChannelKey(channel); + } else { + _channelKeys[channel.toLower()] = key; + } +} + +void CoreNetwork::removeChannelKey(const QString &channel) { + _channelKeys.remove(channel.toLower()); +} + +bool CoreNetwork::setAutoWhoDone(const QString &channel) { + if(_autoWhoInProgress.value(channel.toLower(), 0) <= 0) + return false; + _autoWhoInProgress[channel.toLower()]--; + return true; +} + +void CoreNetwork::setMyNick(const QString &mynick) { + Network::setMyNick(mynick); + if(connectionState() == Network::Initializing) + networkInitialized(); +} + +void CoreNetwork::socketHasData() { + while(socket.canReadLine()) { + QByteArray s = socket.readLine().trimmed(); + ircServerHandler()->handleServerMsg(s); + } +} + +void CoreNetwork::socketError(QAbstractSocket::SocketError) { + _previousConnectionAttemptFailed = true; + qWarning() << qPrintable(tr("Could not connect to %1 (%2)").arg(networkName(), socket.errorString())); + emit connectionError(socket.errorString()); + emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString())); + emitConnectionError(socket.errorString()); + if(socket.state() < QAbstractSocket::ConnectedState) { + socketDisconnected(); + } +} + +#ifdef HAVE_SSL +void CoreNetwork::sslSocketConnected() { + if(!usedServer().useSsl) + socketInitialized(); + else + socket.startClientEncryption(); +} +#endif + +void CoreNetwork::socketInitialized() { + Identity *identity = identityPtr(); + if(!identity) { + qCritical() << "Identity invalid!"; + disconnectFromIrc(); + return; + } + + // TokenBucket to avoid sending too much at once + _messagesPerSecond = 1; + _burstSize = 5; + _tokenBucket = 5; // init with a full bucket + _tokenBucketTimer.start(_messagesPerSecond * 1000); + + QString passwd = usedServer().password; + if(!passwd.isEmpty()) { + putRawLine(serverEncode(QString("PASS %1").arg(passwd))); + } + putRawLine(serverEncode(QString("NICK :%1").arg(identity->nicks()[0]))); + putRawLine(serverEncode(QString("USER %1 8 * :%2").arg(identity->ident(), identity->realName()))); +} + +void CoreNetwork::socketDisconnected() { + _pingTimer.stop(); + _autoWhoCycleTimer.stop(); + _autoWhoTimer.stop(); + _autoWhoQueue.clear(); + _autoWhoInProgress.clear(); + + _socketCloseTimer.stop(); + + _tokenBucketTimer.stop(); + + IrcUser *me_ = me(); + if(me_) { + foreach(QString channel, me_->channels()) + emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, "", me_->hostmask()); + } + + setConnected(false); + emit disconnected(networkId()); + if(_quitRequested) { + setConnectionState(Network::Disconnected); + Core::setNetworkConnected(userId(), networkId(), false); + } else if(_autoReconnectCount != 0) { + setConnectionState(Network::Reconnecting); + if(_autoReconnectCount == autoReconnectRetries()) + doAutoReconnect(); // first try is immediate + else + _autoReconnectTimer.start(); + } +} + +void CoreNetwork::socketStateChanged(QAbstractSocket::SocketState socketState) { + Network::ConnectionState state; + switch(socketState) { + case QAbstractSocket::UnconnectedState: + state = Network::Disconnected; + break; + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + state = Network::Connecting; + break; + case QAbstractSocket::ConnectedState: + state = Network::Initializing; + break; + case QAbstractSocket::ClosingState: + state = Network::Disconnecting; + break; + default: + state = Network::Disconnected; + } + setConnectionState(state); +} + +void CoreNetwork::networkInitialized() { + setConnectionState(Network::Initialized); + setConnected(true); + + if(useAutoReconnect()) { + // reset counter + _autoReconnectCount = autoReconnectRetries(); + } + + sendPerform(); + + _pingTimer.start(); + + if(_autoWhoEnabled) { + _autoWhoCycleTimer.start(); + _autoWhoTimer.start(); + startAutoWhoCycle(); // FIXME wait for autojoin to be completed + } + + Core::bufferInfo(userId(), networkId(), BufferInfo::StatusBuffer); // create status buffer + Core::setNetworkConnected(userId(), networkId(), true); +} + +void CoreNetwork::sendPerform() { + BufferInfo statusBuf = BufferInfo::fakeStatusBuffer(networkId()); + + // do auto identify + if(useAutoIdentify() && !autoIdentifyService().isEmpty() && !autoIdentifyPassword().isEmpty()) { + userInputHandler()->handleMsg(statusBuf, QString("%1 IDENTIFY %2").arg(autoIdentifyService(), autoIdentifyPassword())); + } + + // send perform list + foreach(QString line, perform()) { + if(!line.isEmpty()) userInput(statusBuf, line); + } + + // rejoin channels we've been in + QStringList channels, keys; + foreach(QString chan, persistentChannels()) { + QString key = channelKey(chan); + if(!key.isEmpty()) { + channels.prepend(chan); + keys.prepend(key); + } else { + channels.append(chan); + } + } + QString joinString = QString("%1 %2").arg(channels.join(",")).arg(keys.join(",")).trimmed(); + if(!joinString.isEmpty()) + userInputHandler()->handleJoin(statusBuf, joinString); +} + +void CoreNetwork::setUseAutoReconnect(bool use) { + Network::setUseAutoReconnect(use); + if(!use) + _autoReconnectTimer.stop(); +} + +void CoreNetwork::setAutoReconnectInterval(quint32 interval) { + Network::setAutoReconnectInterval(interval); + _autoReconnectTimer.setInterval(interval * 1000); +} + +void CoreNetwork::setAutoReconnectRetries(quint16 retries) { + Network::setAutoReconnectRetries(retries); + if(_autoReconnectCount != 0) { + if(unlimitedReconnectRetries()) + _autoReconnectCount = -1; + else + _autoReconnectCount = autoReconnectRetries(); + } +} + +void CoreNetwork::doAutoReconnect() { + if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) { + qWarning() << "CoreNetwork::doAutoReconnect(): Cannot reconnect while not being disconnected!"; + return; + } + if(_autoReconnectCount > 0) + _autoReconnectCount--; + connectToIrc(true); +} + +void CoreNetwork::sendPing() { + userInputHandler()->handlePing(BufferInfo(), QString()); +} + +void CoreNetwork::sendAutoWho() { + while(!_autoWhoQueue.isEmpty()) { + QString chan = _autoWhoQueue.takeFirst(); + IrcChannel *ircchan = ircChannel(chan); + if(!ircchan) continue; + if(_autoWhoNickLimit > 0 && ircchan->ircUsers().count() > _autoWhoNickLimit) continue; + _autoWhoInProgress[chan]++; + putRawLine("WHO " + serverEncode(chan)); + if(_autoWhoQueue.isEmpty() && _autoWhoEnabled && !_autoWhoCycleTimer.isActive()) { + // Timer was stopped, means a new cycle is due immediately + _autoWhoCycleTimer.start(); + startAutoWhoCycle(); + } + break; + } +} + +void CoreNetwork::startAutoWhoCycle() { + if(!_autoWhoQueue.isEmpty()) { + _autoWhoCycleTimer.stop(); + return; + } + _autoWhoQueue = channels(); +} + +#ifdef HAVE_SSL +void CoreNetwork::sslErrors(const QList &sslErrors) { + Q_UNUSED(sslErrors) + socket.ignoreSslErrors(); + /* TODO errorhandling + QVariantMap errmsg; + QVariantList errnums; + foreach(QSslError err, errors) errnums << err.error(); + errmsg["SslErrors"] = errnums; + errmsg["SslCert"] = socket.peerCertificate().toPem(); + errmsg["PeerAddress"] = socket.peerAddress().toString(); + errmsg["PeerPort"] = socket.peerPort(); + errmsg["PeerName"] = socket.peerName(); + emit sslErrors(errmsg); + disconnectFromIrc(); + */ +} +#endif // HAVE_SSL + +void CoreNetwork::fillBucketAndProcessQueue() { + if(_tokenBucket < _burstSize) { + _tokenBucket++; + } + + while(_msgQueue.size() > 0 && _tokenBucket > 0) { + writeToSocket(_msgQueue.takeFirst()); + } +} + +void CoreNetwork::writeToSocket(const QByteArray &data) { + socket.write(data); + socket.write("\r\n"); + _tokenBucket--; } void CoreNetwork::requestConnect() const { @@ -35,7 +500,7 @@ void CoreNetwork::requestConnect() const { qWarning() << "Requesting connect while already being connected!"; return; } - emit connectRequested(networkId()); + Network::requestConnect(); } void CoreNetwork::requestDisconnect() const { @@ -43,7 +508,7 @@ void CoreNetwork::requestDisconnect() const { qWarning() << "Requesting disconnect while not being connected!"; return; } - emit disconnectRequested(networkId()); + userInputHandler()->handleQuit(BufferInfo(), QString()); } void CoreNetwork::requestSetNetworkInfo(const NetworkInfo &info) { diff --git a/src/core/corenetwork.h b/src/core/corenetwork.h index 2989932b..f227de4f 100644 --- a/src/core/corenetwork.h +++ b/src/core/corenetwork.h @@ -24,28 +24,169 @@ #include "network.h" #include "coreircchannel.h" -class CoreSession; +#include + +#ifdef HAVE_SSL +# include +# include +#else +# include +#endif + +#include "coresession.h" + +class Identity; +class IrcServerHandler; +class UserInputHandler; +class CtcpHandler; class CoreNetwork : public Network { Q_OBJECT public: CoreNetwork(const NetworkId &networkid, CoreSession *session); - + ~CoreNetwork(); inline virtual const QMetaObject *syncMetaObject() const { return &Network::staticMetaObject; } + inline Identity *identityPtr() const { return coreSession()->identity(identity()); } inline CoreSession *coreSession() const { return _coreSession; } + inline IrcServerHandler *ircServerHandler() const { return _ircServerHandler; } + inline UserInputHandler *userInputHandler() const { return _userInputHandler; } + inline CtcpHandler *ctcpHandler() const { return _ctcpHandler; } + + //! Decode a string using the server (network) decoding. + inline QString serverDecode(const QByteArray &string) const { return decodeServerString(string); } + + //! Decode a string using a channel-specific encoding if one is set (and use the standard encoding else). + QString channelDecode(const QString &channelName, const QByteArray &string) const; + + //! Decode a string using an IrcUser-specific encoding, if one exists (using the standaed encoding else). + QString userDecode(const QString &userNick, const QByteArray &string) const; + + //! Encode a string using the server (network) encoding. + inline QByteArray serverEncode(const QString &string) const { return encodeServerString(string); } + + //! Encode a string using the channel-specific encoding, if set, and use the standard encoding else. + QByteArray channelEncode(const QString &channelName, const QString &string) const; + + //! Encode a string using the user-specific encoding, if set, and use the standard encoding else. + QByteArray userEncode(const QString &userNick, const QString &string) const; + + inline QString channelKey(const QString &channel) const { return _channelKeys.value(channel.toLower(), QString()); } + inline QStringList persistentChannels() const { return _channelKeys.keys(); } + + inline bool isAutoWhoInProgress(const QString &channel) const { return _autoWhoInProgress.value(channel.toLower(), 0); } + + inline UserId userId() const { return _coreSession->user(); } + public slots: + virtual void setMyNick(const QString &mynick); + virtual void requestConnect() const; virtual void requestDisconnect() const; virtual void requestSetNetworkInfo(const NetworkInfo &info); + virtual void setUseAutoReconnect(bool); + virtual void setAutoReconnectInterval(quint32); + virtual void setAutoReconnectRetries(quint16); + + void connectToIrc(bool reconnecting = false); + void disconnectFromIrc(bool requested = true, const QString &reason = QString()); + + void userInput(BufferInfo bufferInfo, QString msg); + void putRawLine(QByteArray input); + void putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix = QByteArray()); + + void setChannelJoined(const QString &channel); + void setChannelParted(const QString &channel); + void addChannelKey(const QString &channel, const QString &key); + void removeChannelKey(const QString &channel); + + bool setAutoWhoDone(const QString &channel); + + inline const Server &usedServer() const { return serverList()[_lastUsedServerIndex]; } + +signals: + void recvRawServerMsg(QString); + void displayStatusMsg(QString); + void displayMsg(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None); + void disconnected(NetworkId networkId); + void connectionError(const QString &errorMsg); + + void quitRequested(NetworkId networkId); + void sslErrors(const QVariant &errorData); + protected: inline virtual IrcChannel *ircChannelFactory(const QString &channelname) { return new CoreIrcChannel(channelname, this); } +private slots: + void socketHasData(); + void socketError(QAbstractSocket::SocketError); + void socketInitialized(); + inline void socketCloseTimeout() { socket.disconnectFromHost(); } + void socketDisconnected(); + void socketStateChanged(QAbstractSocket::SocketState); + void networkInitialized(); + + void sendPerform(); + void doAutoReconnect(); + void sendPing(); + void sendAutoWho(); + void startAutoWhoCycle(); + +#ifdef HAVE_SSL + void sslSocketConnected(); + void sslErrors(const QList &errors); +#endif + + void fillBucketAndProcessQueue(); + + void writeToSocket(const QByteArray &data); + private: CoreSession *_coreSession; + +#ifdef HAVE_SSL + QSslSocket socket; +#else + QTcpSocket socket; +#endif + + IrcServerHandler *_ircServerHandler; + UserInputHandler *_userInputHandler; + CtcpHandler *_ctcpHandler; + + QHash _channelKeys; // stores persistent channels and their passwords, if any + + QTimer _autoReconnectTimer; + int _autoReconnectCount; + + QTimer _socketCloseTimer; + + /* this flag triggers quitRequested() once the socket is closed + * it is needed to determine whether or not the connection needs to be + * in the automatic session restore. */ + bool _quitRequested; + + bool _previousConnectionAttemptFailed; + int _lastUsedServerIndex; + + QTimer _pingTimer; + + bool _autoWhoEnabled; + QStringList _autoWhoQueue; + QHash _autoWhoInProgress; + int _autoWhoInterval; + int _autoWhoNickLimit; + int _autoWhoDelay; + QTimer _autoWhoTimer, _autoWhoCycleTimer; + + QTimer _tokenBucketTimer; + int _messagesPerSecond; // token refill speed + int _burstSize; // size of the token bucket + int _tokenBucket; // the virtual bucket that holds the tokens + QList _msgQueue; }; #endif //CORENETWORK_H diff --git a/src/core/coresession.cpp b/src/core/coresession.cpp index bb4b3dd9..2e9184c5 100644 --- a/src/core/coresession.cpp +++ b/src/core/coresession.cpp @@ -22,9 +22,7 @@ #include "core.h" #include "coresession.h" -#include "networkconnection.h" #include "userinputhandler.h" - #include "signalproxy.h" #include "buffersyncer.h" #include "corebacklogmanager.h" @@ -60,11 +58,9 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent) connect(p, SIGNAL(connected()), this, SLOT(clientsConnected())); connect(p, SIGNAL(disconnected()), this, SLOT(clientsDisconnected())); - //p->attachSlot(SIGNAL(disconnectFromNetwork(NetworkId)), this, SLOT(disconnectFromNetwork(NetworkId))); // FIXME p->attachSlot(SIGNAL(sendInput(BufferInfo, QString)), this, SLOT(msgFromClient(BufferInfo, QString))); p->attachSignal(this, SIGNAL(displayMsg(Message))); p->attachSignal(this, SIGNAL(displayStatusMsg(QString, QString))); - p->attachSignal(this, SIGNAL(bufferInfoUpdated(BufferInfo))); p->attachSignal(this, SIGNAL(identityCreated(const Identity &))); p->attachSignal(this, SIGNAL(identityRemoved(IdentityId))); @@ -111,9 +107,6 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent) CoreSession::~CoreSession() { saveSessionState(); - foreach(NetworkConnection *conn, _connections.values()) { - delete conn; - } foreach(CoreNetwork *net, _networks.values()) { delete net; } @@ -124,11 +117,6 @@ CoreNetwork *CoreSession::network(NetworkId id) const { return 0; } -NetworkConnection *CoreSession::networkConnection(NetworkId id) const { - if(_connections.contains(id)) return _connections[id]; - return 0; -} - Identity *CoreSession::identity(IdentityId id) const { if(_identities.contains(id)) return _identities[id]; return 0; @@ -167,67 +155,18 @@ void CoreSession::loadSettings() { } void CoreSession::saveSessionState() const { - } void CoreSession::restoreSessionState() { QList nets = Core::connectedNetworks(user()); + CoreNetwork *net = 0; foreach(NetworkId id, nets) { - connectToNetwork(id); + net = network(id); + Q_ASSERT(net); + net->connectToIrc(); } } -void CoreSession::updateBufferInfo(UserId uid, const BufferInfo &bufinfo) { - if(uid == user()) emit bufferInfoUpdated(bufinfo); -} - -void CoreSession::connectToNetwork(NetworkId id) { - CoreNetwork *net = network(id); - if(!net) { - qWarning() << "Connect to unknown network requested! net:" << id << "user:" << user(); - return; - } - - NetworkConnection *conn = networkConnection(id); - if(!conn) { - conn = new NetworkConnection(net, this); - _connections[id] = conn; - attachNetworkConnection(conn); - } - conn->connectToIrc(); -} - -void CoreSession::attachNetworkConnection(NetworkConnection *conn) { - connect(conn, SIGNAL(connected(NetworkId)), this, SLOT(networkConnected(NetworkId))); - connect(conn, SIGNAL(quitRequested(NetworkId)), this, SLOT(networkDisconnected(NetworkId))); - - // I guess we don't need these anymore, client-side can just connect the network's signals directly - //signalProxy()->attachSignal(conn, SIGNAL(connected(NetworkId)), SIGNAL(networkConnected(NetworkId))); - //signalProxy()->attachSignal(conn, SIGNAL(disconnected(NetworkId)), SIGNAL(networkDisconnected(NetworkId))); - - connect(conn, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)), - this, SLOT(recvMessageFromServer(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags))); - connect(conn, SIGNAL(displayStatusMsg(QString)), this, SLOT(recvStatusMsgFromServer(QString))); - - connect(conn, SIGNAL(nickChanged(const NetworkId &, const QString &, const QString &)), - this, SLOT(renameBuffer(const NetworkId &, const QString &, const QString &))); - connect(conn, SIGNAL(channelJoined(NetworkId, const QString &, const QString &)), - this, SLOT(channelJoined(NetworkId, const QString &, const QString &))); - connect(conn, SIGNAL(channelParted(NetworkId, const QString &)), - this, SLOT(channelParted(NetworkId, const QString &))); -} - -void CoreSession::disconnectFromNetwork(NetworkId id) { - if(!_connections.contains(id)) - return; - - //_connections[id]->disconnectFromIrc(); - _connections[id]->userInputHandler()->handleQuit(BufferInfo(), QString()); -} - -void CoreSession::networkStateRequested() { -} - void CoreSession::addClient(QIODevice *device) { if(!device) { qCritical() << "Invoking CoreSession::addClient with a QObject that is not a QIODevice!"; @@ -254,35 +193,6 @@ void CoreSession::removeClient(QIODevice *iodev) { quInfo() << qPrintable(tr("Client")) << qPrintable(socket->peerAddress().toString()) << qPrintable(tr("disconnected (UserId: %1).").arg(user().toInt())); } -SignalProxy *CoreSession::signalProxy() const { - return _signalProxy; -} - -// FIXME we need a sane way for creating buffers! -void CoreSession::networkConnected(NetworkId networkid) { - Core::bufferInfo(user(), networkid, BufferInfo::StatusBuffer); // create status buffer - Core::setNetworkConnected(user(), networkid, true); -} - -// called now only on /quit and requested disconnects, not on normal disconnects! -void CoreSession::networkDisconnected(NetworkId networkid) { - // if the network has already been removed, we don't have a networkconnection left either, so we don't do anything - // make sure to not depend on the network still existing when calling this function! - if(_connections.contains(networkid)) { - Core::setNetworkConnected(user(), networkid, false); - _connections.take(networkid)->deleteLater(); - } -} - -void CoreSession::channelJoined(NetworkId id, const QString &channel, const QString &key) { - Core::setChannelPersistent(user(), id, channel, true); - Core::setPersistentChannelKey(user(), id, channel, key); -} - -void CoreSession::channelParted(NetworkId id, const QString &channel) { - Core::setChannelPersistent(user(), id, channel, false); -} - QHash CoreSession::persistentChannels(NetworkId id) const { return Core::persistentChannels(user(), id); return QHash(); @@ -290,9 +200,9 @@ QHash CoreSession::persistentChannels(NetworkId id) const { // FIXME switch to BufferId void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg) { - NetworkConnection *conn = networkConnection(bufinfo.networkId()); - if(conn) { - conn->userInput(bufinfo, msg); + CoreNetwork *net = network(bufinfo.networkId()); + if(net) { + net->userInput(bufinfo, msg); } else { qWarning() << "Trying to send to unconnected network:" << msg; } @@ -302,10 +212,10 @@ void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg) { // So this is the perfect place for storing the backlog and log stuff. void CoreSession::recvMessageFromServer(Message::Type type, BufferInfo::Type bufferType, QString target, QString text, QString sender, Message::Flags flags) { - NetworkConnection *netCon = qobject_cast(this->sender()); - Q_ASSERT(netCon); + CoreNetwork *net = qobject_cast(this->sender()); + Q_ASSERT(net); - BufferInfo bufferInfo = Core::bufferInfo(user(), netCon->networkId(), bufferType, target); + BufferInfo bufferInfo = Core::bufferInfo(user(), net->networkId(), bufferType, target); Message msg(bufferInfo, type, text, sender, flags); msg.setMsgId(Core::storeMessage(msg)); Q_ASSERT(msg.msgId() != 0); @@ -313,9 +223,9 @@ void CoreSession::recvMessageFromServer(Message::Type type, BufferInfo::Type buf } void CoreSession::recvStatusMsgFromServer(QString msg) { - NetworkConnection *s = qobject_cast(sender()); - Q_ASSERT(s); - emit displayStatusMsg(s->networkName(), msg); + CoreNetwork *net = qobject_cast(sender()); + Q_ASSERT(net); + emit displayStatusMsg(net->networkName(), msg); } QList CoreSession::buffers() const { @@ -423,8 +333,10 @@ void CoreSession::createNetwork(const NetworkInfo &info_) { id = info.networkId.toInt(); if(!_networks.contains(id)) { CoreNetwork *net = new CoreNetwork(id, this); - connect(net, SIGNAL(connectRequested(NetworkId)), this, SLOT(connectToNetwork(NetworkId))); - connect(net, SIGNAL(disconnectRequested(NetworkId)), this, SLOT(disconnectFromNetwork(NetworkId))); + connect(net, SIGNAL(displayMsg(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags)), + this, SLOT(recvMessageFromServer(Message::Type, BufferInfo::Type, QString, QString, QString, Message::Flags))); + connect(net, SIGNAL(displayStatusMsg(QString)), this, SLOT(recvStatusMsgFromServer(QString))); + net->setNetworkInfo(info); net->setProxy(signalProxy()); _networks[id] = net; @@ -438,25 +350,19 @@ void CoreSession::createNetwork(const NetworkInfo &info_) { void CoreSession::removeNetwork(NetworkId id) { // Make sure the network is disconnected! - NetworkConnection *conn = _connections.value(id, 0); - if(conn) { - if(conn->connectionState() != Network::Disconnected) { - connect(conn, SIGNAL(disconnected(NetworkId)), this, SLOT(destroyNetwork(NetworkId))); - conn->disconnectFromIrc(); - } else { - _connections.take(id)->deleteLater(); // TODO make this saner - destroyNetwork(id); - } + CoreNetwork *net = network(id); + if(!net) + return; + + if(net->connectionState() != Network::Disconnected) { + connect(net, SIGNAL(disconnected(NetworkId)), this, SLOT(destroyNetwork(NetworkId))); + net->disconnectFromIrc(); } else { destroyNetwork(id); } } void CoreSession::destroyNetwork(NetworkId id) { - if(_connections.contains(id)) { - // this can happen if the network was reconnecting while being removed - _connections.take(id)->deleteLater(); - } QList removedBuffers = Core::requestBufferIdsForNetwork(user(), id); Network *net = _networks.take(id); if(net && Core::removeNetwork(user(), id)) { @@ -496,6 +402,7 @@ void CoreSession::removeBufferRequested(BufferId bufferId) { emit bufferRemoved(bufferId); } +// FIXME: use a coreBufferSyncer for this void CoreSession::renameBuffer(const NetworkId &networkId, const QString &newName, const QString &oldName) { BufferId bufferId = Core::renameBuffer(user(), networkId, newName, oldName); if(bufferId.isValid()) { @@ -504,54 +411,46 @@ void CoreSession::renameBuffer(const NetworkId &networkId, const QString &newNam } void CoreSession::clientsConnected() { - QHash::iterator conIter = _connections.begin(); + QHash::iterator netIter = _networks.begin(); Identity *identity = 0; - NetworkConnection *con = 0; - Network *network = 0; + CoreNetwork *net = 0; IrcUser *me = 0; QString awayReason; - while(conIter != _connections.end()) { - con = *conIter; - conIter++; + while(netIter != _networks.end()) { + net = *netIter; + netIter++; - if(!con->isConnected()) + if(!net->isConnected()) continue; - identity = con->identity(); + identity = net->identityPtr(); if(!identity) continue; - network = con->network(); - if(!network) - continue; - me = network->me(); + me = net->me(); if(!me) continue; if(identity->detachAwayEnabled() && me->isAway()) { - con->userInputHandler()->handleAway(BufferInfo(), QString()); + net->userInputHandler()->handleAway(BufferInfo(), QString()); } } } void CoreSession::clientsDisconnected() { - QHash::iterator conIter = _connections.begin(); + QHash::iterator netIter = _networks.begin(); Identity *identity = 0; - NetworkConnection *con = 0; - Network *network = 0; + CoreNetwork *net = 0; IrcUser *me = 0; QString awayReason; - while(conIter != _connections.end()) { - con = *conIter; - conIter++; + while(netIter != _networks.end()) { + net = *netIter; + netIter++; - if(!con->isConnected()) + if(!net->isConnected()) continue; - identity = con->identity(); + identity = net->identityPtr(); if(!identity) continue; - network = con->network(); - if(!network) - continue; - me = network->me(); + me = net->me(); if(!me) continue; @@ -560,8 +459,8 @@ void CoreSession::clientsDisconnected() { awayReason = identity->detachAwayReason(); else awayReason = identity->awayReason(); - network->setAutoAwayActive(true); - con->userInputHandler()->handleAway(BufferInfo(), awayReason); + net->setAutoAwayActive(true); + net->userInputHandler()->handleAway(BufferInfo(), awayReason); } } } diff --git a/src/core/coresession.h b/src/core/coresession.h index 91ad27e1..8fa8725b 100644 --- a/src/core/coresession.h +++ b/src/core/coresession.h @@ -55,28 +55,23 @@ public: QVariant sessionState(); - SignalProxy *signalProxy() const; + inline SignalProxy *signalProxy() const { return _signalProxy; } const AliasManager &aliasManager() const { return _aliasManager; } AliasManager &aliasManager() { return _aliasManager; } inline CoreIrcListHelper *ircListHelper() const { return _ircListHelper; } - void attachNetworkConnection(NetworkConnection *conn); +// void attachNetworkConnection(NetworkConnection *conn); //! Return necessary data for restoring the session after restarting the core void saveSessionState() const; void restoreSessionState(); public slots: - void networkStateRequested(); - void addClient(QIODevice *device); void addClient(SignalProxy *proxy); - void connectToNetwork(NetworkId); - void disconnectFromNetwork(NetworkId id); - void msgFromClient(BufferInfo, QString message); //! Create an identity and propagate the changes to the clients. @@ -113,8 +108,6 @@ public slots: */ void renameBuffer(const NetworkId &networkId, const QString &newName, const QString &oldName); - void channelJoined(NetworkId id, const QString &channel, const QString &key = QString()); - void channelParted(NetworkId, const QString &channel); QHash persistentChannels(NetworkId) const; signals: @@ -125,11 +118,6 @@ signals: void displayMsg(Message message); void displayStatusMsg(QString, QString); - //void connectToIrc(QString net); - //void disconnectFromIrc(QString net); - - void bufferInfoUpdated(BufferInfo); - void scriptResult(QString result); //! Identity has been created. @@ -154,20 +142,11 @@ private slots: void recvStatusMsgFromServer(QString msg); void recvMessageFromServer(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None); - void networkConnected(NetworkId networkid); - void networkDisconnected(NetworkId networkid); void destroyNetwork(NetworkId); void identityUpdated(const QVariantMap &); - //! Called when storage updated a BufferInfo. - /** This emits bufferInfoUpdated() via SignalProxy, iff it's one of our buffers. - * \param user The buffer's owner (not necessarily us) - * \param bufferInfo The updated BufferInfo - */ - void updateBufferInfo(UserId user, const BufferInfo &bufferInfo); - void storeBufferLastSeenMsg(BufferId buffer, const MsgId &msgId); void scriptRequest(QString script); @@ -183,7 +162,7 @@ private: SignalProxy *_signalProxy; CoreAliasManager _aliasManager; - QHash _connections; + // QHash _connections; QHash _networks; // QHash _networksToRemove; QHash _identities; diff --git a/src/core/ctcphandler.cpp b/src/core/ctcphandler.cpp index 1ebbfafb..10dfd9a6 100644 --- a/src/core/ctcphandler.cpp +++ b/src/core/ctcphandler.cpp @@ -24,7 +24,7 @@ #include "quassel.h" #include "util.h" -CtcpHandler::CtcpHandler(NetworkConnection *parent) +CtcpHandler::CtcpHandler(CoreNetwork *parent) : BasicHandler(parent), XDELIM("\001") { diff --git a/src/core/ctcphandler.h b/src/core/ctcphandler.h index cac2f565..d15f6e6e 100644 --- a/src/core/ctcphandler.h +++ b/src/core/ctcphandler.h @@ -30,7 +30,7 @@ class CtcpHandler : public BasicHandler { Q_OBJECT public: - CtcpHandler(NetworkConnection *parent = 0); + CtcpHandler(CoreNetwork *parent = 0); enum CtcpType {CtcpQuery, CtcpReply}; diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index 4f5206ad..06f968cb 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -23,8 +23,6 @@ #include "coresession.h" #include "coreirclisthelper.h" -#include "networkconnection.h" -#include "network.h" #include "identity.h" #include "ctcphandler.h" @@ -34,7 +32,7 @@ #include -IrcServerHandler::IrcServerHandler(NetworkConnection *parent) +IrcServerHandler::IrcServerHandler(CoreNetwork *parent) : BasicHandler(parent), _whois(false) { @@ -153,8 +151,8 @@ void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const // many nets define their own WHOIS fields. we fetch those not in need of special attention here: emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", "[Whois] " + params.join(" "), prefix); } else { - if(networkConnection()->coreSession()->ircListHelper()->requestInProgress(network()->networkId())) - networkConnection()->coreSession()->ircListHelper()->reportError(params.join(" ")); + if(coreSession()->ircListHelper()->requestInProgress(network()->networkId())) + coreSession()->ircListHelper()->reportError(params.join(" ")); else emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", cmd + " " + params.join(" "), prefix); } @@ -179,7 +177,7 @@ void IrcServerHandler::handleJoin(const QString &prefix, const QList //qDebug() << "IrcServerHandler::handleJoin()" << prefix << params; ircuser->joinChannel(channel); if(network()->isMe(ircuser)) { - networkConnection()->setChannelJoined(channel); + network()->setChannelJoined(channel); putCmd("MODE", params[0]); // we want to know the modes of the channel we just joined, so we ask politely } } @@ -203,7 +201,7 @@ void IrcServerHandler::handleKick(const QString &prefix, const QList msg = victim->nick(); emit displayMsg(Message::Kick, BufferInfo::ChannelBuffer, channel, msg, prefix); - //if(network()->isMe(victim)) networkConnection()->setKickedFromChannel(channel); + //if(network()->isMe(victim)) network()->setKickedFromChannel(channel); } void IrcServerHandler::handleMode(const QString &prefix, const QList ¶ms) { @@ -313,7 +311,7 @@ void IrcServerHandler::handleNick(const QString &prefix, const QList ? newnick : prefix; - emit nickChanged(newnick, oldnick); + coreSession()->renameBuffer(network()->networkId(), newnick, oldnick); foreach(QString channel, ircuser->channels()) emit displayMsg(Message::Nick, BufferInfo::ChannelBuffer, channel, newnick, sender); @@ -352,7 +350,7 @@ void IrcServerHandler::handleNotice(const QString &prefix, const QListctcpHandler()->parse(Message::Notice, prefix, target, params[1]); + network()->ctcpHandler()->parse(Message::Notice, prefix, target, params[1]); } void IrcServerHandler::handlePart(const QString &prefix, const QList ¶ms) { @@ -373,7 +371,7 @@ void IrcServerHandler::handlePart(const QString &prefix, const QList msg = userDecode(ircuser->nick(), params[1]); emit displayMsg(Message::Part, BufferInfo::ChannelBuffer, channel, msg, prefix); - if(network()->isMe(ircuser)) networkConnection()->setChannelParted(channel); + if(network()->isMe(ircuser)) network()->setChannelParted(channel); } void IrcServerHandler::handlePing(const QString &prefix, const QList ¶ms) { @@ -424,7 +422,7 @@ void IrcServerHandler::handlePrivmsg(const QString &prefix, const QList we let the ctcpHandler do the work - networkConnection()->ctcpHandler()->parse(Message::Plain, prefix, target, msg); + network()->ctcpHandler()->parse(Message::Plain, prefix, target, msg); } void IrcServerHandler::handleQuit(const QString &prefix, const QList ¶ms) { @@ -706,7 +704,7 @@ void IrcServerHandler::handle315(const QString &prefix, const QList return; QStringList p = serverDecode(params); - if(networkConnection()->setAutoWhoDone(p[0])) { + if(network()->setAutoWhoDone(p[0])) { return; // stay silent } p.takeLast(); // should be "End of WHO list" @@ -798,7 +796,7 @@ void IrcServerHandler::handle322(const QString &prefix, const QList default: break; } - if(!networkConnection()->coreSession()->ircListHelper()->addChannel(network()->networkId(), channelName, userCount, topic)) + if(!coreSession()->ircListHelper()->addChannel(network()->networkId(), channelName, userCount, topic)) emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Channel %1 has %2 users. Topic is: %3").arg(channelName).arg(userCount).arg(topic)); } @@ -807,7 +805,7 @@ void IrcServerHandler::handle323(const QString &prefix, const QList Q_UNUSED(prefix) Q_UNUSED(params) - if(!networkConnection()->coreSession()->ircListHelper()->endOfChannelList(network()->networkId())) + if(!coreSession()->ircListHelper()->endOfChannelList(network()->networkId())) emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("End of channel list")); } @@ -886,7 +884,7 @@ void IrcServerHandler::handle352(const QString &prefix, const QList ircuser->setRealName(serverDecode(params.last()).section(" ", 1)); } - if(!networkConnection()->isAutoWhoInProgress(channel)) { + if(!network()->isAutoWhoInProgress(channel)) { emit displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("[Who] %1").arg(serverDecode(params).join(" "))); } } @@ -976,7 +974,7 @@ void IrcServerHandler::handle433(const QString &prefix, const QList /* */ void IrcServerHandler::tryNextNick(const QString &errnick) { - QStringList desiredNicks = networkConnection()->coreSession()->identity(network()->identity())->nicks(); + QStringList desiredNicks = coreSession()->identity(network()->identity())->nicks(); int nextNick = desiredNicks.indexOf(errnick) + 1; if(desiredNicks.size() > nextNick) { putCmd("NICK", serverEncode(desiredNicks[nextNick])); diff --git a/src/core/ircserverhandler.h b/src/core/ircserverhandler.h index 9624928c..406b0a38 100644 --- a/src/core/ircserverhandler.h +++ b/src/core/ircserverhandler.h @@ -18,8 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#ifndef _IRCSERVERHANDLER_H_ -#define _IRCSERVERHANDLER_H_ +#ifndef IRCSERVERHANDLER_H +#define IRCSERVERHANDLER_H #include "basichandler.h" @@ -27,7 +27,7 @@ class IrcServerHandler : public BasicHandler { Q_OBJECT public: - IrcServerHandler(NetworkConnection *parent); + IrcServerHandler(CoreNetwork *parent); ~IrcServerHandler(); void handleServerMsg(QByteArray rawMsg); @@ -80,9 +80,6 @@ public slots: void defaultHandler(QString cmd, const QString &prefix, const QList ¶ms); -signals: - void nickChanged(const QString &newNick, const QString &oldNick); // this signal is inteded to rename query buffers in the storage backend - private: void tryNextNick(const QString &errnick); bool checkParamCount(const QString &methodName, const QList ¶ms, int minParams); diff --git a/src/core/networkconnection.cpp b/src/core/networkconnection.cpp deleted file mode 100644 index d177d4c1..00000000 --- a/src/core/networkconnection.cpp +++ /dev/null @@ -1,593 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005-08 by the Quassel Project * - * devel@quassel-irc.org * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) version 3. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#include "networkconnection.h" - -#include -#include -#include - -#include "util.h" -#include "core.h" -#include "coresession.h" - -#include "ircchannel.h" -#include "ircuser.h" -#include "identity.h" - -#include "ircserverhandler.h" -#include "userinputhandler.h" -#include "ctcphandler.h" - -#include "logger.h" - -NetworkConnection::NetworkConnection(Network *network, CoreSession *session) - : QObject(network), - _connectionState(Network::Disconnected), - _network(network), - _coreSession(session), - _ircServerHandler(new IrcServerHandler(this)), - _userInputHandler(new UserInputHandler(this)), - _ctcpHandler(new CtcpHandler(this)), - _autoReconnectCount(0), - _quitRequested(false), - - _previousConnectionAttemptFailed(false), - _lastUsedServerlistIndex(0), - - // TODO make autowho configurable (possibly per-network) - _autoWhoEnabled(true), - _autoWhoInterval(90), - _autoWhoNickLimit(0), // unlimited - _autoWhoDelay(3), - - // TokenBucket to avaid sending too much at once - _messagesPerSecond(1), - _burstSize(5), - _tokenBucket(5) // init with a full bucket -{ - _autoReconnectTimer.setSingleShot(true); - _socketCloseTimer.setSingleShot(true); - connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); - - _pingTimer.setInterval(60000); - connect(&_pingTimer, SIGNAL(timeout()), this, SLOT(sendPing())); - - _autoWhoTimer.setInterval(_autoWhoDelay * 1000); - _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); - - _tokenBucketTimer.start(_messagesPerSecond * 1000); - - QHash channels = coreSession()->persistentChannels(networkId()); - foreach(QString chan, channels.keys()) { - _channelKeys[chan.toLower()] = channels[chan]; - } - - connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect())); - connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho())); - connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle())); - connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue())); - - connect(network, SIGNAL(currentServerSet(const QString &)), this, SLOT(networkInitialized(const QString &))); - connect(network, SIGNAL(useAutoReconnectSet(bool)), this, SLOT(autoReconnectSettingsChanged())); - connect(network, SIGNAL(autoReconnectIntervalSet(quint32)), this, SLOT(autoReconnectSettingsChanged())); - connect(network, SIGNAL(autoReconnectRetriesSet(quint16)), this, SLOT(autoReconnectSettingsChanged())); - -#ifdef HAVE_SSL - connect(&socket, SIGNAL(encrypted()), this, SLOT(socketEncrypted())); - connect(&socket, SIGNAL(sslErrors(const QList &)), this, SLOT(sslErrors(const QList &))); -#endif - connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected())); - - connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); - connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); - connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); - connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData())); - - connect(_ircServerHandler, SIGNAL(nickChanged(const QString &, const QString &)), - this, SLOT(nickChanged(const QString &, const QString &))); - - network->proxy()->attachSignal(this, SIGNAL(sslErrors(const QVariant &))); -} - -NetworkConnection::~NetworkConnection() { - if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) - disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! - disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up - delete _ircServerHandler; - delete _userInputHandler; - delete _ctcpHandler; -} - -void NetworkConnection::setConnectionState(Network::ConnectionState state) { - _connectionState = state; - network()->setConnectionState(state); - emit connectionStateChanged(state); -} - -QString NetworkConnection::serverDecode(const QByteArray &string) const { - return network()->decodeServerString(string); -} - -QString NetworkConnection::channelDecode(const QString &bufferName, const QByteArray &string) const { - if(!bufferName.isEmpty()) { - IrcChannel *channel = network()->ircChannel(bufferName); - if(channel) return channel->decodeString(string); - } - return network()->decodeString(string); -} - -QString NetworkConnection::userDecode(const QString &userNick, const QByteArray &string) const { - IrcUser *user = network()->ircUser(userNick); - if(user) return user->decodeString(string); - return network()->decodeString(string); -} - -QByteArray NetworkConnection::serverEncode(const QString &string) const { - return network()->encodeServerString(string); -} - -QByteArray NetworkConnection::channelEncode(const QString &bufferName, const QString &string) const { - if(!bufferName.isEmpty()) { - IrcChannel *channel = network()->ircChannel(bufferName); - if(channel) return channel->encodeString(string); - } - return network()->encodeString(string); -} - -QByteArray NetworkConnection::userEncode(const QString &userNick, const QString &string) const { - IrcUser *user = network()->ircUser(userNick); - if(user) return user->encodeString(string); - return network()->encodeString(string); -} - -void NetworkConnection::autoReconnectSettingsChanged() { - if(!network()->useAutoReconnect()) { - _autoReconnectTimer.stop(); - _autoReconnectCount = 0; - } else { - _autoReconnectTimer.setInterval(network()->autoReconnectInterval() * 1000); - if(_autoReconnectCount != 0) { - if(network()->unlimitedReconnectRetries()) _autoReconnectCount = -1; - else _autoReconnectCount = network()->autoReconnectRetries(); - } - } -} - -void NetworkConnection::connectToIrc(bool reconnecting) { - if(!reconnecting && network()->useAutoReconnect() && _autoReconnectCount == 0) { - _autoReconnectTimer.setInterval(network()->autoReconnectInterval() * 1000); - if(network()->unlimitedReconnectRetries()) _autoReconnectCount = -1; - else _autoReconnectCount = network()->autoReconnectRetries(); - } - QVariantList serverList = network()->serverList(); - Identity *identity = coreSession()->identity(network()->identity()); - if(!serverList.count()) { - qWarning() << "Server list empty, ignoring connect request!"; - return; - } - if(!identity) { - qWarning() << "Invalid identity configures, ignoring connect request!"; - return; - } - // use a random server? - if(network()->useRandomServer()) { - _lastUsedServerlistIndex = qrand() % serverList.size(); - } else if(_previousConnectionAttemptFailed) { - // cycle to next server if previous connection attempt failed - displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Connection failed. Cycling to next Server")); - if(++_lastUsedServerlistIndex == serverList.size()) { - _lastUsedServerlistIndex = 0; - } - } - _previousConnectionAttemptFailed = false; - - QString host = serverList[_lastUsedServerlistIndex].toMap()["Host"].toString(); - quint16 port = serverList[_lastUsedServerlistIndex].toMap()["Port"].toUInt(); - displayStatusMsg(tr("Connecting to %1:%2...").arg(host).arg(port)); - displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Connecting to %1:%2...").arg(host).arg(port)); - socket.connectToHost(host, port); -} - -void NetworkConnection::networkInitialized(const QString ¤tServer) { - if(currentServer.isEmpty()) return; - - if(network()->useAutoReconnect() && !network()->unlimitedReconnectRetries()) { - _autoReconnectCount = network()->autoReconnectRetries(); // reset counter - } - - sendPerform(); - - // now we are initialized - setConnectionState(Network::Initialized); - network()->setConnected(true); - emit connected(networkId()); - - _pingTimer.start(); - - if(_autoWhoEnabled) { - _autoWhoCycleTimer.start(); - _autoWhoTimer.start(); - startAutoWhoCycle(); // FIXME wait for autojoin to be completed - } -} - -void NetworkConnection::sendPerform() { - BufferInfo statusBuf = Core::bufferInfo(coreSession()->user(), network()->networkId(), BufferInfo::StatusBuffer); - // do auto identify - if(network()->useAutoIdentify() && !network()->autoIdentifyService().isEmpty() && !network()->autoIdentifyPassword().isEmpty()) { - userInputHandler()->handleMsg(statusBuf, QString("%1 IDENTIFY %2").arg(network()->autoIdentifyService(), network()->autoIdentifyPassword())); - } - // send perform list - foreach(QString line, network()->perform()) { - if(!line.isEmpty()) userInput(statusBuf, line); - } - - // rejoin channels we've been in - QStringList channels, keys; - foreach(QString chan, persistentChannels()) { - QString key = channelKey(chan); - if(!key.isEmpty()) { - channels.prepend(chan); keys.prepend(key); - } else { - channels.append(chan); - } - } - QString joinString = QString("%1 %2").arg(channels.join(",")).arg(keys.join(",")).trimmed(); - if(!joinString.isEmpty()) userInputHandler()->handleJoin(statusBuf, joinString); -} - -void NetworkConnection::disconnectFromIrc(bool requested, const QString &reason) { - _quitRequested = requested; // see socketDisconnected(); - _autoReconnectTimer.stop(); - _autoReconnectCount = 0; // prohibiting auto reconnect - displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting.")); - if(socket.state() == QAbstractSocket::UnconnectedState) { - socketDisconnected(); - } else if(socket.state() < QAbstractSocket::ConnectedState || !requested) { - // we might be in a state waiting for a timeout... - // or (!requested) this is a core shutdown... - // in both cases we don't really care... set a disconnected state - socket.close(); - socketDisconnected(); - } else { - // quit gracefully if it's user requested quit - userInputHandler()->issueQuit(reason); - // the irc server has 10 seconds to close the socket - _socketCloseTimer.start(10000); - } -} - -void NetworkConnection::socketHasData() { - while(socket.canReadLine()) { - QByteArray s = socket.readLine().trimmed(); - ircServerHandler()->handleServerMsg(s); - } -} - -void NetworkConnection::socketError(QAbstractSocket::SocketError) { - _previousConnectionAttemptFailed = true; - qWarning() << qPrintable(tr("Could not connect to %1 (%2)").arg(network()->networkName(), socket.errorString())); - emit connectionError(socket.errorString()); - emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString())); - network()->emitConnectionError(socket.errorString()); - if(socket.state() < QAbstractSocket::ConnectedState) { - socketDisconnected(); - } - // mark last connection attempt as failed - - //qDebug() << "exiting..."; - //exit(1); -} - -#ifdef HAVE_SSL - -void NetworkConnection::sslErrors(const QList &sslErrors) { - Q_UNUSED(sslErrors) - socket.ignoreSslErrors(); - /* TODO errorhandling - QVariantMap errmsg; - QVariantList errnums; - foreach(QSslError err, errors) errnums << err.error(); - errmsg["SslErrors"] = errnums; - errmsg["SslCert"] = socket.peerCertificate().toPem(); - errmsg["PeerAddress"] = socket.peerAddress().toString(); - errmsg["PeerPort"] = socket.peerPort(); - errmsg["PeerName"] = socket.peerName(); - emit sslErrors(errmsg); - disconnectFromIrc(); - */ -} - -void NetworkConnection::socketEncrypted() { - //qDebug() << "encrypted!"; - socketInitialized(); -} - -#endif // HAVE_SSL - -void NetworkConnection::socketConnected() { -#ifndef HAVE_SSL - socketInitialized(); - return; -#else - if(!network()->serverList()[_lastUsedServerlistIndex].toMap()["UseSSL"].toBool()) { - socketInitialized(); - return; - } - //qDebug() << "starting handshake"; - socket.startClientEncryption(); -#endif -} - -void NetworkConnection::socketInitialized() { - //emit connected(networkId()); initialize first! - Identity *identity = coreSession()->identity(network()->identity()); - if(!identity) { - qCritical() << "Identity invalid!"; - disconnectFromIrc(); - return; - } - QString passwd = network()->serverList()[_lastUsedServerlistIndex].toMap()["Password"].toString(); - if(!passwd.isEmpty()) { - putRawLine(serverEncode(QString("PASS %1").arg(passwd))); - } - putRawLine(serverEncode(QString("NICK :%1").arg(identity->nicks()[0]))); // FIXME: try more nicks if error occurs - putRawLine(serverEncode(QString("USER %1 8 * :%2").arg(identity->ident(), identity->realName()))); -} - -void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketState) { - Network::ConnectionState state; - switch(socketState) { - case QAbstractSocket::UnconnectedState: - state = Network::Disconnected; - break; - case QAbstractSocket::HostLookupState: - case QAbstractSocket::ConnectingState: - state = Network::Connecting; - break; - case QAbstractSocket::ConnectedState: - state = Network::Initializing; - break; - case QAbstractSocket::ClosingState: - state = Network::Disconnecting; - break; - default: - state = Network::Disconnected; - } - setConnectionState(state); -} - -void NetworkConnection::socketCloseTimeout() { - socket.disconnectFromHost(); -} - -void NetworkConnection::socketDisconnected() { - _pingTimer.stop(); - _autoWhoCycleTimer.stop(); - _autoWhoTimer.stop(); - _autoWhoQueue.clear(); - _autoWhoInProgress.clear(); - - _socketCloseTimer.stop(); - - IrcUser *me = network()->me(); - if(me) { - foreach(QString channel, me->channels()) - emit displayMsg(Message::Quit, BufferInfo::ChannelBuffer, channel, "", me->hostmask()); - } - - network()->setConnected(false); - emit disconnected(networkId()); - if(_quitRequested) { - setConnectionState(Network::Disconnected); - emit quitRequested(networkId()); - } else if(_autoReconnectCount != 0) { - setConnectionState(Network::Reconnecting); - if(_autoReconnectCount == network()->autoReconnectRetries()) - doAutoReconnect(); // first try is immediate - else - _autoReconnectTimer.start(); - } -} - -void NetworkConnection::doAutoReconnect() { - if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) { - qWarning() << "NetworkConnection::doAutoReconnect(): Cannot reconnect while not being disconnected!"; - return; - } - if(_autoReconnectCount > 0) _autoReconnectCount--; - connectToIrc(true); -} - -// FIXME switch to BufferId -void NetworkConnection::userInput(BufferInfo buf, QString msg) { - userInputHandler()->handleUserInput(buf, msg); -} - -void NetworkConnection::putRawLine(QByteArray s) { - if(_tokenBucket > 0) { - // qDebug() << "putRawLine: " << s; - writeToSocket(s); - } else { - _msgQueue.append(s); - } -} - -void NetworkConnection::writeToSocket(QByteArray s) { - s += "\r\n"; - // qDebug() << "writeToSocket: " << s.size(); - socket.write(s); - _tokenBucket--; -} - -void NetworkConnection::fillBucketAndProcessQueue() { - if(_tokenBucket < _burstSize) { - _tokenBucket++; - } - - while(_msgQueue.size() > 0 && _tokenBucket > 0) { - writeToSocket(_msgQueue.takeFirst()); - } -} - -// returns 0 if the message will not be chopped by the irc server or number of chopped bytes if message is too long -int NetworkConnection::lastParamOverrun(const QString &cmd, const QList ¶ms) { - //the server will pass our message that trunkated to 512 bytes including CRLF with the following format: - // ":prefix COMMAND param0 param1 :lastparam" - // where prefix = "nickname!user@host" - // that means that the last message can be as long as: - // 512 - nicklen - userlen - hostlen - commandlen - sum(param[0]..param[n-1])) - 2 (for CRLF) - 4 (":!@" + 1space between prefix and command) - max(paramcount - 1, 0) (space for simple params) - 2 (space and colon for last param) - IrcUser *me = network()->me(); - int maxLen = 480 - cmd.toAscii().count(); // educated guess in case we don't know us (yet?) - - if(me) - maxLen = 512 - serverEncode(me->nick()).count() - serverEncode(me->user()).count() - serverEncode(me->host()).count() - cmd.toAscii().count() - 6; - - if(!params.isEmpty()) { - for(int i = 0; i < params.count() - 1; i++) { - maxLen -= (params[i].count() + 1); - } - maxLen -= 2; // " :" last param separator; - - if(params.last().count() > maxLen) { - return params.last().count() - maxLen; - } else { - return 0; - } - } else { - return 0; - } -} - -void NetworkConnection::putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix) { - QByteArray msg; - if(cmd == "PRIVMSG" && params.count() > 1) { - int overrun = lastParamOverrun(cmd, params); - if(overrun) { - QList paramCopy1 = params; - paramCopy1.removeLast(); - QList paramCopy2 = paramCopy1; - - QByteArray lastPart = params.last(); - QByteArray splitter(" .,-"); - int maxSplitPos = params.last().count() - overrun; - int splitPos = -1; - for(int i = 0; i < splitter.size(); i++) { - splitPos = qMax(splitPos, lastPart.lastIndexOf(splitter[i], maxSplitPos)); - } - if(splitPos <= 0) { - splitPos = maxSplitPos; - } - - paramCopy1 << lastPart.left(splitPos); - paramCopy2 << lastPart.mid(splitPos); - putCmd(cmd, paramCopy1, prefix); - putCmd(cmd, paramCopy2, prefix); - return; - } - } - - if(!prefix.isEmpty()) - msg += ":" + prefix + " "; - msg += cmd.toUpper().toAscii(); - - for(int i = 0; i < params.size() - 1; i++) { - msg += " " + params[i]; - } - if(!params.isEmpty()) - msg += " :" + params.last(); - - putRawLine(msg); -} - -void NetworkConnection::sendPing() { - userInputHandler()->handlePing(BufferInfo(), QString()); -} - -void NetworkConnection::sendAutoWho() { - while(!_autoWhoQueue.isEmpty()) { - QString chan = _autoWhoQueue.takeFirst(); - IrcChannel *ircchan = network()->ircChannel(chan); - if(!ircchan) continue; - if(_autoWhoNickLimit > 0 && ircchan->ircUsers().count() > _autoWhoNickLimit) continue; - _autoWhoInProgress[chan]++; - putRawLine("WHO " + serverEncode(chan)); - if(_autoWhoQueue.isEmpty() && _autoWhoEnabled && !_autoWhoCycleTimer.isActive()) { - // Timer was stopped, means a new cycle is due immediately - _autoWhoCycleTimer.start(); - startAutoWhoCycle(); - } - break; - } -} - -void NetworkConnection::startAutoWhoCycle() { - if(!_autoWhoQueue.isEmpty()) { - _autoWhoCycleTimer.stop(); - return; - } - _autoWhoQueue = network()->channels(); -} - -bool NetworkConnection::setAutoWhoDone(const QString &channel) { - if(_autoWhoInProgress.value(channel.toLower(), 0) <= 0) return false; - _autoWhoInProgress[channel.toLower()]--; - return true; -} - -void NetworkConnection::setChannelJoined(const QString &channel) { - emit channelJoined(networkId(), channel, _channelKeys[channel.toLower()]); - _autoWhoQueue.prepend(channel.toLower()); // prepend so this new chan is the first to be checked -} - -void NetworkConnection::setChannelParted(const QString &channel) { - removeChannelKey(channel); - _autoWhoQueue.removeAll(channel.toLower()); - _autoWhoInProgress.remove(channel.toLower()); - emit channelParted(networkId(), channel); -} - -void NetworkConnection::addChannelKey(const QString &channel, const QString &key) { - if(key.isEmpty()) { - removeChannelKey(channel); - } else { - _channelKeys[channel.toLower()] = key; - } -} - -void NetworkConnection::removeChannelKey(const QString &channel) { - _channelKeys.remove(channel.toLower()); -} - -void NetworkConnection::nickChanged(const QString &newNick, const QString &oldNick) { - emit nickChanged(networkId(), newNick, oldNick); -} - -/* Exception classes for message handling */ -NetworkConnection::ParseError::ParseError(QString cmd, QString prefix, QStringList params) { - Q_UNUSED(prefix); - _msg = QString("Command Parse Error: ") + cmd + params.join(" "); -} - -NetworkConnection::UnknownCmdError::UnknownCmdError(QString cmd, QString prefix, QStringList params) { - Q_UNUSED(prefix); - _msg = QString("Unknown Command: ") + cmd + params.join(" "); -} - diff --git a/src/core/networkconnection.h b/src/core/networkconnection.h deleted file mode 100644 index d9474557..00000000 --- a/src/core/networkconnection.h +++ /dev/null @@ -1,215 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2005-08 by the Quassel Project * - * devel@quassel-irc.org * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) version 3. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -#ifndef _NETWORKCONNECTION_H_ -#define _NETWORKCONNECTION_H_ - -#include -#include -#include -#include - -#ifdef HAVE_SSL -# include -# include -#else -# include -#endif - -#include "coresession.h" -#include "identity.h" -#include "message.h" -#include "network.h" -#include "signalproxy.h" - -class IrcServerHandler; -class UserInputHandler; -class CtcpHandler; - -class NetworkConnection : public QObject { - Q_OBJECT - -public: - NetworkConnection(Network *network, CoreSession *session); - ~NetworkConnection(); - - inline NetworkId networkId() const { return network()->networkId(); } - inline QString networkName() const { return network()->networkName(); } - inline Network *network() const { return _network; } - inline Identity *identity() const { return coreSession()->identity(network()->identity()); } - inline CoreSession *coreSession() const { return _coreSession; } - - inline bool isConnected() const { return connectionState() == Network::Initialized; } - inline Network::ConnectionState connectionState() const { return _connectionState; } - - inline IrcServerHandler *ircServerHandler() const { return _ircServerHandler; } - inline UserInputHandler *userInputHandler() const { return _userInputHandler; } - inline CtcpHandler *ctcpHandler() const { return _ctcpHandler; } - - //! Decode a string using the server (network) decoding. - QString serverDecode(const QByteArray &string) const; - - //! Decode a string using a channel-specific encoding if one is set (and use the standard encoding else). - QString channelDecode(const QString &channelName, const QByteArray &string) const; - - //! Decode a string using an IrcUser-specific encoding, if one exists (using the standaed 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 channel-specific encoding, if set, and use the standard encoding else. - QByteArray channelEncode(const QString &channelName, const QString &string) const; - - //! Encode a string using the user-specific encoding, if set, and use the standard encoding else. - QByteArray userEncode(const QString &userNick, const QString &string) const; - - inline QString channelKey(const QString &channel) const { return _channelKeys.value(channel.toLower(), QString()); } - inline QStringList persistentChannels() const { return _channelKeys.keys(); } - - inline bool isAutoWhoInProgress(const QString &channel) const { return _autoWhoInProgress.value(channel.toLower(), 0); } - -public slots: - // void setServerOptions(); - void connectToIrc(bool reconnecting = false); - void disconnectFromIrc(bool requested = true, const QString &reason = QString()); - void userInput(BufferInfo bufferInfo, QString msg); - - void putRawLine(QByteArray input); - int lastParamOverrun(const QString &cmd, const QList ¶ms); - void putCmd(const QString &cmd, const QList ¶ms, const QByteArray &prefix = QByteArray()); - - void setChannelJoined(const QString &channel); - void setChannelParted(const QString &channel); - void addChannelKey(const QString &channel, const QString &key); - void removeChannelKey(const QString &channel); - - bool setAutoWhoDone(const QString &channel); - -signals: - // #void networkState(QString net, QVariantMap data); - void recvRawServerMsg(QString); - void displayStatusMsg(QString); - //void displayMsg(Message msg); - void displayMsg(Message::Type, BufferInfo::Type, QString target, QString text, QString sender = "", Message::Flags flags = Message::None); - void connected(NetworkId networkId); ///< Emitted after receipt of 001 to indicate that we can now send data to the IRC server - void disconnected(NetworkId networkId); - void connectionStateChanged(Network::ConnectionState); - void connectionInitialized(); ///< Emitted after receipt of 001 to indicate that we can now send data to the IRC server - void connectionError(const QString &errorMsg); - - void quitRequested(NetworkId networkId); - - //void queryRequested(QString network, QString nick); - void nickChanged(const NetworkId &networkId, const QString &newNick, const QString &oldNick); // this signal is inteded to rename query buffers in the storage backend - void channelJoined(NetworkId, const QString &channel, const QString &key = QString()); - void channelParted(NetworkId, const QString &channel); - - void sslErrors(const QVariant &errorData); - -private slots: - void socketHasData(); - void socketError(QAbstractSocket::SocketError); - void socketConnected(); - void socketInitialized(); - void socketCloseTimeout(); - void socketDisconnected(); - void socketStateChanged(QAbstractSocket::SocketState); - void setConnectionState(Network::ConnectionState); - void networkInitialized(const QString ¤tServer); - - void sendPerform(); - void autoReconnectSettingsChanged(); - void doAutoReconnect(); - void sendPing(); - void sendAutoWho(); - void startAutoWhoCycle(); - void nickChanged(const QString &newNick, const QString &oldNick); // this signal is inteded to rename query buffers in the storage backend - -#ifdef HAVE_SSL - void socketEncrypted(); - void sslErrors(const QList &errors); -#endif - - void fillBucketAndProcessQueue(); - -private: -#ifdef HAVE_SSL - QSslSocket socket; -#else - QTcpSocket socket; -#endif - - Network::ConnectionState _connectionState; - - Network *_network; - CoreSession *_coreSession; - BufferInfo _statusBufferInfo; - - IrcServerHandler *_ircServerHandler; - UserInputHandler *_userInputHandler; - CtcpHandler *_ctcpHandler; - - QHash _channelKeys; // stores persistent channels and their passwords, if any - - QTimer _autoReconnectTimer; - - int _autoReconnectCount; - - QTimer _socketCloseTimer; - - /* this flag triggers quitRequested() once the socket is closed - * it is needed to determine whether or not the connection needs to be - *in the automatic session restore. */ - bool _quitRequested; - - bool _previousConnectionAttemptFailed; - int _lastUsedServerlistIndex; - - QTimer _pingTimer; - - bool _autoWhoEnabled; - QStringList _autoWhoQueue; - QHash _autoWhoInProgress; - int _autoWhoInterval; - int _autoWhoNickLimit; - int _autoWhoDelay; - QTimer _autoWhoTimer, _autoWhoCycleTimer; - - QTimer _tokenBucketTimer; - int _messagesPerSecond; // token refill speed - int _burstSize; // size of the token bucket - int _tokenBucket; // the virtual bucket that holds the tokens - QList _msgQueue; - - void writeToSocket(QByteArray s); - - class ParseError : public Exception { - public: - ParseError(QString cmd, QString prefix, QStringList params); - }; - - class UnknownCmdError : public Exception { - public: - UnknownCmdError(QString cmd, QString prefix, QStringList params); - }; -}; - -#endif diff --git a/src/core/userinputhandler.cpp b/src/core/userinputhandler.cpp index 44373b60..8e6896fe 100644 --- a/src/core/userinputhandler.cpp +++ b/src/core/userinputhandler.cpp @@ -21,15 +21,16 @@ #include "util.h" -#include "networkconnection.h" -#include "network.h" #include "ctcphandler.h" +#include "identity.h" #include "ircuser.h" #include #include -UserInputHandler::UserInputHandler(NetworkConnection *parent) : BasicHandler(parent) { +UserInputHandler::UserInputHandler(CoreNetwork *parent) + : BasicHandler(parent) +{ } void UserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QString &msg_) { @@ -58,7 +59,6 @@ void UserInputHandler::handleUserInput(const BufferInfo &bufferInfo, const QStri // ==================== // Public Slots // ==================== - void UserInputHandler::handleAway(const BufferInfo &bufferInfo, const QString &msg) { Q_UNUSED(bufferInfo) @@ -68,7 +68,7 @@ void UserInputHandler::handleAway(const BufferInfo &bufferInfo, const QString &m // if there is no message supplied we have to check if we are already away or not if(msg.isEmpty()) { if(me && !me->isAway()) - awayMsg = networkConnection()->identity()->awayReason(); + awayMsg = network()->identityPtr()->awayReason(); } if(me) me->setAwayMessage(awayMsg); @@ -124,9 +124,12 @@ void UserInputHandler::banOrUnban(const BufferInfo &bufferInfo, const QString &m void UserInputHandler::handleCtcp(const BufferInfo &bufferInfo, const QString &msg) { Q_UNUSED(bufferInfo) + QString nick = msg.section(' ', 0, 0); QString ctcpTag = msg.section(' ', 1, 1).toUpper(); - if (ctcpTag.isEmpty()) return; + if(ctcpTag.isEmpty()) + return; + QString message = ""; QString verboseMessage = tr("sending CTCP-%1 request").arg(ctcpTag); @@ -135,7 +138,7 @@ void UserInputHandler::handleCtcp(const BufferInfo &bufferInfo, const QString &m message = QString::number(now); } - networkConnection()->ctcpHandler()->query(nick, ctcpTag, message); + network()->ctcpHandler()->query(nick, ctcpTag, message); emit displayMsg(Message::Action, BufferInfo::StatusBuffer, "", verboseMessage, network()->myNick()); } @@ -181,10 +184,10 @@ void UserInputHandler::handleJoin(const BufferInfo &bufferInfo, const QString &m i = 0; for(; i < keys.count(); i++) { if(i >= chans.count()) break; - networkConnection()->addChannelKey(chans[i], keys[i]); + network()->addChannelKey(chans[i], keys[i]); } for(; i < chans.count(); i++) { - networkConnection()->removeChannelKey(chans[i]); + network()->removeChannelKey(chans[i]); } } @@ -192,7 +195,7 @@ void UserInputHandler::handleKick(const BufferInfo &bufferInfo, const QString &m QString nick = msg.section(' ', 0, 0, QString::SectionSkipEmpty); QString reason = msg.section(' ', 1, -1, QString::SectionSkipEmpty).trimmed(); if(reason.isEmpty()) - reason = networkConnection()->identity()->kickReason(); + reason = network()->identityPtr()->kickReason(); QList params; params << serverEncode(bufferInfo.bufferName()) << serverEncode(nick) << channelEncode(bufferInfo.bufferName(), reason); @@ -216,7 +219,7 @@ void UserInputHandler::handleList(const BufferInfo &bufferInfo, const QString &m void UserInputHandler::handleMe(const BufferInfo &bufferInfo, const QString &msg) { if(bufferInfo.bufferName().isEmpty()) return; // server buffer - networkConnection()->ctcpHandler()->query(bufferInfo.bufferName(), "ACTION", msg); + network()->ctcpHandler()->query(bufferInfo.bufferName(), "ACTION", msg); emit displayMsg(Message::Action, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); } @@ -238,11 +241,8 @@ void UserInputHandler::handleMsg(const BufferInfo &bufferInfo, const QString &ms if(!msg.contains(' ')) return; - QList params; - params << serverEncode(msg.section(' ', 0, 0)); - params << userEncode(params[0], msg.section(' ', 1)); - - emit putCmd("PRIVMSG", params); + QByteArray target = serverEncode(msg.section(' ', 0, 0)); + putPrivmsg(target, userEncode(target, msg.section(' ', 1))); } void UserInputHandler::handleNick(const BufferInfo &bufferInfo, const QString &msg) { @@ -287,7 +287,7 @@ void UserInputHandler::handlePart(const BufferInfo &bufferInfo, const QString &m } if(partReason.isEmpty()) - partReason = networkConnection()->identity()->partReason(); + partReason = network()->identityPtr()->partReason(); params << serverEncode(channelName) << channelEncode(bufferInfo.bufferName(), partReason); emit putCmd("PART", params); @@ -317,13 +317,13 @@ void UserInputHandler::handleQuery(const BufferInfo &bufferInfo, const QString & void UserInputHandler::handleQuit(const BufferInfo &bufferInfo, const QString &msg) { Q_UNUSED(bufferInfo) - networkConnection()->disconnectFromIrc(true, msg); + network()->disconnectFromIrc(true, msg); } void UserInputHandler::issueQuit(const QString &reason) { QString quitReason; if(reason.isEmpty()) - quitReason = networkConnection()->identity()->quitReason(); + quitReason = network()->identityPtr()->quitReason(); else quitReason = reason; emit putCmd("QUIT", serverEncode(quitReason)); @@ -335,10 +335,9 @@ void UserInputHandler::handleQuote(const BufferInfo &bufferInfo, const QString & } void UserInputHandler::handleSay(const BufferInfo &bufferInfo, const QString &msg) { - if(bufferInfo.bufferName().isEmpty()) return; // server buffer - QList params; - params << serverEncode(bufferInfo.bufferName()) << channelEncode(bufferInfo.bufferName(), msg); - emit putCmd("PRIVMSG", params); + if(bufferInfo.bufferName().isEmpty()) + return; // server buffer + putPrivmsg(serverEncode(bufferInfo.bufferName()), channelEncode(bufferInfo.bufferName(), msg)); emit displayMsg(Message::Plain, bufferInfo.type(), bufferInfo.bufferName(), msg, network()->myNick(), Message::Self); } @@ -422,4 +421,53 @@ void UserInputHandler::expand(const QString &alias, const BufferInfo &bufferInfo } +void UserInputHandler::putPrivmsg(const QByteArray &target, const QByteArray &message) { + static const char *cmd = "PRIVMSG"; + int overrun = lastParamOverrun(cmd, QList() << message); + if(overrun) { + static const char *splitter = " .,-"; + int maxSplitPos = message.count() - overrun; + int splitPos = -1; + for(const char *splitChar = splitter; *splitChar != 0; splitChar++) { + splitPos = qMax(splitPos, message.lastIndexOf(*splitChar, maxSplitPos)); + } + if(splitPos <= 0) { + splitPos = maxSplitPos; + } + putCmd(cmd, QList() << target << message.left(splitPos)); + putPrivmsg(target, message.mid(splitPos)); + return; + } else { + putCmd(cmd, QList() << target << message); + } +} + +// returns 0 if the message will not be chopped by the irc server or number of chopped bytes if message is too long +int UserInputHandler::lastParamOverrun(const QString &cmd, const QList ¶ms) { + // the server will pass our message trunkated to 512 bytes including CRLF with the following format: + // ":prefix COMMAND param0 param1 :lastparam" + // where prefix = "nickname!user@host" + // that means that the last message can be as long as: + // 512 - nicklen - userlen - hostlen - commandlen - sum(param[0]..param[n-1])) - 2 (for CRLF) - 4 (":!@" + 1space between prefix and command) - max(paramcount - 1, 0) (space for simple params) - 2 (space and colon for last param) + IrcUser *me = network()->me(); + int maxLen = 480 - cmd.toAscii().count(); // educated guess in case we don't know us (yet?) + + if(me) + maxLen = 512 - serverEncode(me->nick()).count() - serverEncode(me->user()).count() - serverEncode(me->host()).count() - cmd.toAscii().count() - 6; + + if(!params.isEmpty()) { + for(int i = 0; i < params.count() - 1; i++) { + maxLen -= (params[i].count() + 1); + } + maxLen -= 2; // " :" last param separator; + + if(params.last().count() > maxLen) { + return params.last().count() - maxLen; + } else { + return 0; + } + } else { + return 0; + } +} diff --git a/src/core/userinputhandler.h b/src/core/userinputhandler.h index f8f936f0..f7bd01dc 100644 --- a/src/core/userinputhandler.h +++ b/src/core/userinputhandler.h @@ -29,7 +29,7 @@ class UserInputHandler : public BasicHandler { Q_OBJECT public: - UserInputHandler(NetworkConnection *parent = 0); + UserInputHandler(CoreNetwork *parent = 0); void handleUserInput(const BufferInfo &bufferInfo, const QString &text); @@ -71,6 +71,8 @@ public slots: private: void expand(const QString &alias, const BufferInfo &bufferInfo, const QString &msg); void banOrUnban(const BufferInfo &bufferInfo, const QString &text, bool ban); + void putPrivmsg(const QByteArray &target, const QByteArray &message); + int lastParamOverrun(const QString &cmd, const QList ¶ms); };