Rename socketConnected() to socketOpen() to reduce confusion
[quassel.git] / src / core / corenetwork.cpp
index 3a2c208..4223c5f 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2012 by the Quassel Project                        *
+ *   Copyright (C) 2005-2014 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -18,6 +18,8 @@
  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
  ***************************************************************************/
 
+#include <QHostInfo>
+
 #include "corenetwork.h"
 
 #include "core.h"
@@ -40,10 +42,10 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
 
     _lastPingTime(0),
     _pingCount(0),
+    _sendPings(false),
     _requestedUserModes('-')
 {
     _autoReconnectTimer.setSingleShot(true);
-    _socketCloseTimer.setSingleShot(true);
     connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout()));
 
     setPingInterval(networkConfig()->pingInterval());
@@ -80,7 +82,7 @@ CoreNetwork::CoreNetwork(const NetworkId &networkid, CoreSession *session)
     connect(this, SIGNAL(newEvent(Event *)), coreSession()->eventManager(), SLOT(postEvent(Event *)));
 
     if (Quassel::isOptionSet("oidentd")) {
-        connect(this, SIGNAL(socketInitialized(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection);
+        connect(this, SIGNAL(socketOpen(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(addSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Qt::BlockingQueuedConnection);
         connect(this, SIGNAL(socketDisconnected(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)), Core::instance()->oidentdConfigGenerator(), SLOT(removeSocket(const CoreIdentity*, QHostAddress, quint16, QHostAddress, quint16)));
     }
 }
@@ -182,8 +184,13 @@ void CoreNetwork::connectToIrc(bool reconnecting)
         socket.setProxy(QNetworkProxy::NoProxy);
     }
 
+    enablePingTimeout();
+
+    // Qt caches DNS entries for a minute, resulting in round-robin (e.g. for chat.freenode.net) not working if several users
+    // connect at a similar time. QHostInfo::fromName(), however, always performs a fresh lookup, overwriting the cache entry.
+    QHostInfo::fromName(server.host);
+
 #ifdef HAVE_SSL
-    socket.setProtocol((QSsl::SslProtocol)server.sslVersion);
     if (server.useSsl) {
         CoreIdentity *identity = identityPtr();
         if (identity) {
@@ -261,13 +268,16 @@ void CoreNetwork::putCmd(const QString &cmd, const QList<QByteArray> &params, co
 
     if (!prefix.isEmpty())
         msg += ":" + prefix + " ";
-    msg += cmd.toUpper().toAscii();
+    msg += cmd.toUpper().toLatin1();
+
+    for (int i = 0; i < params.size(); i++) {
+        msg += " ";
 
-    for (int i = 0; i < params.size() - 1; i++) {
-        msg += " " + params[i];
+        if (i == params.size() - 1 && (params[i].contains(' ') || (!params[i].isEmpty() && params[i][0] == ':')))
+            msg += ":";
+
+        msg += params[i];
     }
-    if (!params.isEmpty())
-        msg += " :" + params.last();
 
     putRawLine(msg);
 }
@@ -310,7 +320,7 @@ void CoreNetwork::removeChannelKey(const QString &channel)
 
 
 #ifdef HAVE_QCA2
-Cipher *CoreNetwork::cipher(const QString &target) const
+Cipher *CoreNetwork::cipher(const QString &target)
 {
     if (target.isEmpty())
         return 0;
@@ -318,39 +328,64 @@ Cipher *CoreNetwork::cipher(const QString &target) const
     if (!Cipher::neededFeaturesAvailable())
         return 0;
 
-    QByteArray key = cipherKey(target);
-    if (key.isEmpty())
-        return 0;
-
     CoreIrcChannel *channel = qobject_cast<CoreIrcChannel *>(ircChannel(target));
     if (channel) {
-        if (channel->cipher()->setKey(key))
-            return channel->cipher();
+        return channel->cipher();
     }
-    else {
-        CoreIrcUser *user = qobject_cast<CoreIrcUser *>(ircUser(target));
-        if (user && user->cipher()->setKey(key))
-            return user->cipher();
+    CoreIrcUser *user = qobject_cast<CoreIrcUser *>(ircUser(target));
+    if (user) {
+        return user->cipher();
+    } else if (!isChannelName(target)) {
+        return qobject_cast<CoreIrcUser*>(newIrcUser(target))->cipher();
     }
     return 0;
 }
 
 
-QByteArray CoreNetwork::cipherKey(const QString &recipient) const
+QByteArray CoreNetwork::cipherKey(const QString &target) const
 {
-    return _cipherKeys.value(recipient.toLower(), QByteArray());
+    CoreIrcChannel *c = qobject_cast<CoreIrcChannel*>(ircChannel(target));
+    if (c)
+        return c->cipher()->key();
+
+    CoreIrcUser *u = qobject_cast<CoreIrcUser*>(ircUser(target));
+    if (u)
+        return u->cipher()->key();
+
+    return QByteArray();
 }
 
 
-void CoreNetwork::setCipherKey(const QString &recipient, const QByteArray &key)
+void CoreNetwork::setCipherKey(const QString &target, const QByteArray &key)
 {
-    if (!key.isEmpty())
-        _cipherKeys[recipient.toLower()] = key;
-    else
-        _cipherKeys.remove(recipient.toLower());
+    CoreIrcChannel *c = qobject_cast<CoreIrcChannel*>(ircChannel(target));
+    if (c) {
+        c->setEncrypted(c->cipher()->setKey(key));
+        return;
+    }
+
+    CoreIrcUser *u = qobject_cast<CoreIrcUser*>(ircUser(target));
+    if (!u && !isChannelName(target))
+        u = qobject_cast<CoreIrcUser*>(newIrcUser(target));
+
+    if (u) {
+        u->setEncrypted(u->cipher()->setKey(key));
+        return;
+    }
 }
 
 
+bool CoreNetwork::cipherUsesCBC(const QString &target)
+{
+    CoreIrcChannel *c = qobject_cast<CoreIrcChannel*>(ircChannel(target));
+    if (c)
+        return c->cipher()->usesCBC();
+    CoreIrcUser *u = qobject_cast<CoreIrcUser*>(ircUser(target));
+    if (u)
+        return u->cipher()->usesCBC();
+
+    return false;
+}
 #endif /* HAVE_QCA2 */
 
 bool CoreNetwork::setAutoWhoDone(const QString &channel)
@@ -376,7 +411,10 @@ void CoreNetwork::socketHasData()
 {
     while (socket.canReadLine()) {
         QByteArray s = socket.readLine();
-        s.chop(2);
+        if (s.endsWith("\r\n"))
+            s.chop(2);
+        else if (s.endsWith("\n"))
+            s.chop(1);
         NetworkDataEvent *event = new NetworkDataEvent(EventManager::NetworkIncoming, this, s);
 #if QT_VERSION >= 0x040700
         event->setTimestamp(QDateTime::currentDateTimeUtc());
@@ -406,18 +444,23 @@ void CoreNetwork::socketError(QAbstractSocket::SocketError error)
 
 void CoreNetwork::socketInitialized()
 {
-    Server server = usedServer();
-#ifdef HAVE_SSL
-    if (server.useSsl && !socket.isEncrypted())
-        return;
-#endif
-
     CoreIdentity *identity = identityPtr();
     if (!identity) {
         qCritical() << "Identity invalid!";
         disconnectFromIrc();
         return;
     }
+    
+    emit socketOpen(identity, localAddress(), localPort(), peerAddress(), peerPort());
+    
+    Server server = usedServer();
+#ifdef HAVE_SSL
+    if (server.useSsl && !socket.isEncrypted())
+        return;
+#endif
+#if QT_VERSION >= 0x040600
+    socket.setSocketOption(QAbstractSocket::KeepAliveOption, true);
+#endif
 
     emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort());
 
@@ -526,7 +569,7 @@ void CoreNetwork::networkInitialized()
 
     sendPerform();
 
-    enablePingTimeout();
+    _sendPings = true;
 
     if (networkConfig()->autoWhoEnabled()) {
         _autoWhoCycleTimer.start();
@@ -770,7 +813,9 @@ void CoreNetwork::sendPing()
     else {
         _lastPingTime = now;
         _pingCount++;
-        userInputHandler()->handlePing(BufferInfo(), QString());
+        // Don't send pings until the network is initialized
+        if(_sendPings)
+            userInputHandler()->handlePing(BufferInfo(), QString());
     }
 }
 
@@ -790,6 +835,7 @@ void CoreNetwork::enablePingTimeout(bool enable)
 void CoreNetwork::disablePingTimeout()
 {
     _pingTimer.stop();
+    _sendPings = false;
     resetPingTimeout();
 }