Properly handle RPC calls that return void
[quassel.git] / src / common / signalproxy.cpp
index 418fdec..e085cbb 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2013 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  *
@@ -34,6 +34,7 @@
 #include "protocol.h"
 #include "syncableobject.h"
 #include "util.h"
+#include "types.h"
 
 using namespace Protocol;
 
@@ -95,7 +96,11 @@ void SignalProxy::SignalRelay::attachSignal(QObject *sender, int signalId, const
     }
     else {
         fn = SIGNAL(fakeMethodSignature());
+#if QT_VERSION >= 0x050000
+        fn = fn.replace("fakeMethodSignature()", sender->metaObject()->method(signalId).methodSignature());
+#else
         fn = fn.replace("fakeMethodSignature()", sender->metaObject()->method(signalId).signature());
+#endif
     }
 
     _slots[slotId] = Signal(sender, signalId, fn);
@@ -140,14 +145,22 @@ int SignalProxy::SignalRelay::qt_metacall(QMetaObject::Call _c, int _id, void **
             const QList<int> &argTypes = eMeta->argTypes(signal.signalId);
             for (int i = 0; i < argTypes.size(); i++) {
                 if (argTypes[i] == 0) {
+#if QT_VERSION >= 0x050000
+                    qWarning() << "SignalRelay::qt_metacall(): received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(caller->metaObject()->className()).arg(caller->metaObject()->method(_id).methodSignature().constData());
+#else
                     qWarning() << "SignalRelay::qt_metacall(): received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(caller->metaObject()->className()).arg(caller->metaObject()->method(_id).signature());
+#endif
                     qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
                     return _id;
                 }
                 params << QVariant(argTypes[i], _a[i+1]);
             }
 
-            proxy()->dispatch(RpcCall(signal.signature, params));
+            if (argTypes.size() >= 1 && argTypes[0] == qMetaTypeId<PeerPtr>() && proxy()->proxyMode() == SignalProxy::Server) {
+                Peer *peer = params[0].value<PeerPtr>();
+                proxy()->dispatch(peer, RpcCall(signal.signature, params));
+            } else
+                proxy()->dispatch(RpcCall(signal.signature, params));
         }
         _id -= _slots.count();
     }
@@ -502,6 +515,16 @@ void SignalProxy::dispatch(const T &protoMessage)
 }
 
 
+template<class T>
+void SignalProxy::dispatch(Peer *peer, const T &protoMessage)
+{
+    if (peer && peer->isOpen())
+        peer->dispatch(protoMessage);
+    else
+        QCoreApplication::postEvent(this, new ::RemovePeerEvent(peer));
+}
+
+
 void SignalProxy::handle(Peer *peer, const SyncMessage &syncMessage)
 {
     if (!_syncSlave.contains(syncMessage.className) || !_syncSlave[syncMessage.className].contains(syncMessage.objectName)) {
@@ -524,8 +547,13 @@ void SignalProxy::handle(Peer *peer, const SyncMessage &syncMessage)
         return;
     }
 
-    QVariant returnValue((QVariant::Type)eMeta->returnType(slotId));
-    if (!invokeSlot(receiver, slotId, syncMessage.params, returnValue)) {
+    // We can no longer construct a QVariant from QMetaType::Void
+    QVariant returnValue;
+    int returnType = eMeta->returnType(slotId);
+    if (returnType != QMetaType::Void)
+        returnValue = QVariant(static_cast<QVariant::Type>(returnType));
+
+    if (!invokeSlot(receiver, slotId, syncMessage.params, returnValue, peer)) {
         qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", eMeta->methodName(slotId).constData());
         return;
     }
@@ -586,15 +614,13 @@ void SignalProxy::handle(Peer *peer, const InitData &initData)
 
 void SignalProxy::handle(Peer *peer, const RpcCall &rpcCall)
 {
-    Q_UNUSED(peer)
-
     QObject *receiver;
     int methodId;
     SlotHash::const_iterator slot = _attachedSlots.constFind(rpcCall.slotName);
     while (slot != _attachedSlots.constEnd() && slot.key() == rpcCall.slotName) {
         receiver = (*slot).first;
         methodId = (*slot).second;
-        if (!invokeSlot(receiver, methodId, rpcCall.params)) {
+        if (!invokeSlot(receiver, methodId, rpcCall.params, peer)) {
             ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
             qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", eMeta->methodName(methodId).constData());
         }
@@ -603,7 +629,7 @@ void SignalProxy::handle(Peer *peer, const RpcCall &rpcCall)
 }
 
 
-bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, QVariant &returnValue)
+bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, QVariant &returnValue, Peer *peer)
 {
     ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
     const QList<int> args = eMeta->argTypes(methodId);
@@ -623,7 +649,11 @@ bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList
     // check for argument compatibility and build params array
     for (int i = 0; i < numArgs; i++) {
         if (!params[i].isValid()) {
+#if QT_VERSION >= 0x050000
+            qWarning() << "SignalProxy::invokeSlot(): received invalid data for argument number" << i << "of method" << QString("%1::%2()").arg(receiver->metaObject()->className()).arg(receiver->metaObject()->method(methodId).methodSignature().constData());
+#else
             qWarning() << "SignalProxy::invokeSlot(): received invalid data for argument number" << i << "of method" << QString("%1::%2()").arg(receiver->metaObject()->className()).arg(receiver->metaObject()->method(methodId).signature());
+#endif
             qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
             return false;
         }
@@ -631,7 +661,12 @@ bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList
             qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << eMeta->methodName(methodId);
             return false;
         }
-        _a[i+1] = const_cast<void *>(params[i].constData());
+        // if first arg is a PeerPtr, replace it by the address of the peer originally receiving the RpcCall
+        if (peer && i == 0 && args[0] == qMetaTypeId<PeerPtr>()) {
+            QVariant v = QVariant::fromValue<PeerPtr>(peer);
+            _a[1] = const_cast<void*>(v.constData());
+        } else
+            _a[i+1] = const_cast<void *>(params[i].constData());
     }
 
     if (returnValue.type() != QVariant::Invalid)
@@ -652,10 +687,10 @@ bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList
 }
 
 
-bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params)
+bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, Peer *peer)
 {
     QVariant ret;
-    return invokeSlot(receiver, methodId, params, ret);
+    return invokeSlot(receiver, methodId, params, ret, peer);
 }
 
 
@@ -723,7 +758,11 @@ void SignalProxy::sync_call__(const SyncableObject *obj, SignalProxy::ProxyMode
         params << QVariant(argTypes[i], va_arg(ap, void *));
     }
 
-    dispatch(SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
+    if (argTypes.size() >= 1 && argTypes[0] == qMetaTypeId<PeerPtr>() && proxyMode() == SignalProxy::Server) {
+        Peer *peer = params[0].value<PeerPtr>();
+        dispatch(peer, SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
+    } else
+        dispatch(SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
 }
 
 
@@ -783,7 +822,11 @@ SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta, boo
         if (_meta->method(i).methodType() != QMetaMethod::Slot)
             continue;
 
+#if QT_VERSION >= 0x050000
+        if (_meta->method(i).methodSignature().contains('*'))
+#else
         if (QByteArray(_meta->method(i).signature()).contains('*'))
+#endif
             continue;  // skip methods with ptr params
 
         QByteArray method = methodName(_meta->method(i));
@@ -812,7 +855,11 @@ SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta, boo
             }
             if (checkConflicts) {
                 qWarning() << "class" << meta->className() << "contains overloaded methods which is currently not supported!";
+#if QT_VERSION >= 0x050000
+                qWarning() << " - " << _meta->method(i).methodSignature() << "conflicts with" << _meta->method(_methodIds[method]).methodSignature();
+#else
                 qWarning() << " - " << _meta->method(i).signature() << "conflicts with" << _meta->method(_methodIds[method]).signature();
+#endif
             }
             continue;
         }
@@ -852,7 +899,11 @@ const QHash<int, int> &SignalProxy::ExtendedMetaObject::receiveMap()
             if (QMetaType::Void == (QMetaType::Type)returnType(i))
                 continue;
 
+#if QT_VERSION >= 0x050000
+            signature = requestSlot.methodSignature();
+#else
             signature = QByteArray(requestSlot.signature());
+#endif
             if (!signature.startsWith("request"))
                 continue;
 
@@ -886,14 +937,22 @@ const QHash<int, int> &SignalProxy::ExtendedMetaObject::receiveMap()
 
 QByteArray SignalProxy::ExtendedMetaObject::methodName(const QMetaMethod &method)
 {
+#if QT_VERSION >= 0x050000
+    QByteArray sig(method.methodSignature());
+#else
     QByteArray sig(method.signature());
+#endif
     return sig.left(sig.indexOf("("));
 }
 
 
 QString SignalProxy::ExtendedMetaObject::methodBaseName(const QMetaMethod &method)
 {
+#if QT_VERSION >= 0x050000
+    QString methodname = QString(method.methodSignature()).section("(", 0, 0);
+#else
     QString methodname = QString(method.signature()).section("(", 0, 0);
+#endif
 
     // determine where we have to chop:
     int upperCharPos;
@@ -931,7 +990,11 @@ SignalProxy::ExtendedMetaObject::MethodDescriptor::MethodDescriptor(const QMetaM
     _argTypes = argTypes;
 
     // determine minArgCount
+#if QT_VERSION >= 0x050000
+    QString signature(method.methodSignature());
+#else
     QString signature(method.signature());
+#endif
     _minArgCount = method.parameterTypes().count() - signature.count("=");
 
     _receiverMode = (_methodName.startsWith("request"))