Implement UI and serialization logic for sender modes
[quassel.git] / src / common / signalproxy.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QCoreApplication>
22 #include <QHostAddress>
23 #include <QMetaMethod>
24 #include <QMetaProperty>
25 #include <QThread>
26
27 #ifdef HAVE_SSL
28     #include <QSslSocket>
29 #endif
30
31 #include "signalproxy.h"
32
33 #include "peer.h"
34 #include "protocol.h"
35 #include "syncableobject.h"
36 #include "util.h"
37 #include "types.h"
38
39 using namespace Protocol;
40
41 class RemovePeerEvent : public QEvent
42 {
43 public:
44     RemovePeerEvent(Peer *peer) : QEvent(QEvent::Type(SignalProxy::RemovePeerEvent)), peer(peer) {}
45     Peer *peer;
46 };
47
48
49 // ==================================================
50 //  SignalRelay
51 // ==================================================
52 class SignalProxy::SignalRelay : public QObject
53 {
54 /* Q_OBJECT is not necessary or even allowed, because we implement
55    qt_metacall ourselves (and don't use any other features of the meta
56    object system)
57 */
58 public:
59     SignalRelay(SignalProxy *parent) : QObject(parent), _proxy(parent) {}
60     inline SignalProxy *proxy() const { return _proxy; }
61
62     int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
63
64     void attachSignal(QObject *sender, int signalId, const QByteArray &funcName);
65     void detachSignal(QObject *sender, int signalId = -1);
66
67 private:
68     struct Signal {
69         QObject *sender;
70         int signalId;
71         QByteArray signature;
72         Signal(QObject *sender, int sigId, const QByteArray &signature) : sender(sender), signalId(sigId), signature(signature) {}
73         Signal() : sender(0), signalId(-1) {}
74     };
75
76     SignalProxy *_proxy;
77     QHash<int, Signal> _slots;
78 };
79
80
81 void SignalProxy::SignalRelay::attachSignal(QObject *sender, int signalId, const QByteArray &funcName)
82 {
83     // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
84     // all connected methodIds are offset by the standard methodCount of QObject
85     int slotId;
86     for (int i = 0;; i++) {
87         if (!_slots.contains(i)) {
88             slotId = i;
89             break;
90         }
91     }
92
93     QByteArray fn;
94     if (!funcName.isEmpty()) {
95         fn = QMetaObject::normalizedSignature(funcName);
96     }
97     else {
98         fn = SIGNAL(fakeMethodSignature());
99 #if QT_VERSION >= 0x050000
100         fn = fn.replace("fakeMethodSignature()", sender->metaObject()->method(signalId).methodSignature());
101 #else
102         fn = fn.replace("fakeMethodSignature()", sender->metaObject()->method(signalId).signature());
103 #endif
104     }
105
106     _slots[slotId] = Signal(sender, signalId, fn);
107
108     QMetaObject::connect(sender, signalId, this, QObject::staticMetaObject.methodCount() + slotId);
109 }
110
111
112 void SignalProxy::SignalRelay::detachSignal(QObject *sender, int signalId)
113 {
114     QHash<int, Signal>::iterator slotIter = _slots.begin();
115     while (slotIter != _slots.end()) {
116         if (slotIter->sender == sender && (signalId == -1 || slotIter->signalId == signalId)) {
117             slotIter = _slots.erase(slotIter);
118             if (signalId != -1)
119                 break;
120         }
121         else {
122             ++slotIter;
123         }
124     }
125 }
126
127
128 int SignalProxy::SignalRelay::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
129 {
130     _id = QObject::qt_metacall(_c, _id, _a);
131     if (_id < 0)
132         return _id;
133
134     if (_c == QMetaObject::InvokeMetaMethod) {
135         if (_slots.contains(_id)) {
136             QObject *caller = sender();
137
138             SignalProxy::ExtendedMetaObject *eMeta = proxy()->extendedMetaObject(caller->metaObject());
139             Q_ASSERT(eMeta);
140
141             const Signal &signal = _slots[_id];
142
143             QVariantList params;
144
145             const QList<int> &argTypes = eMeta->argTypes(signal.signalId);
146             for (int i = 0; i < argTypes.size(); i++) {
147                 if (argTypes[i] == 0) {
148 #if QT_VERSION >= 0x050000
149                     qWarning() << "SignalRelay::qt_metacall(): received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(caller->metaObject()->className()).arg(caller->metaObject()->method(signal.signalId).methodSignature().constData());
150 #else
151                     qWarning() << "SignalRelay::qt_metacall(): received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(caller->metaObject()->className()).arg(caller->metaObject()->method(signal.signalId).signature());
152 #endif
153                     qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
154                     return _id;
155                 }
156                 params << QVariant(argTypes[i], _a[i+1]);
157             }
158
159             if (proxy()->_restrictMessageTarget) {
160                 for (auto peer : proxy()->_restrictedTargets) {
161                     if (peer != nullptr)
162                         proxy()->dispatch(peer, RpcCall(signal.signature, params));
163                 }
164             } else
165                 proxy()->dispatch(RpcCall(signal.signature, params));
166         }
167         _id -= _slots.count();
168     }
169     return _id;
170 }
171
172
173 // ==================================================
174 //  SignalProxy
175 // ==================================================
176 SignalProxy::SignalProxy(QObject *parent)
177     : QObject(parent)
178 {
179     setProxyMode(Client);
180     init();
181 }
182
183
184 SignalProxy::SignalProxy(ProxyMode mode, QObject *parent)
185     : QObject(parent)
186 {
187     setProxyMode(mode);
188     init();
189 }
190
191
192 SignalProxy::~SignalProxy()
193 {
194     QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
195     while (classIter != _syncSlave.end()) {
196         ObjectId::iterator objIter = classIter->begin();
197         while (objIter != classIter->end()) {
198             SyncableObject *obj = objIter.value();
199             objIter = classIter->erase(objIter);
200             obj->stopSynchronize(this);
201         }
202         ++classIter;
203     }
204     _syncSlave.clear();
205
206     removeAllPeers();
207 }
208
209
210 void SignalProxy::setProxyMode(ProxyMode mode)
211 {
212     if (!_peerMap.empty()) {
213         qWarning() << Q_FUNC_INFO << "Cannot change proxy mode while connected";
214         return;
215     }
216
217     _proxyMode = mode;
218     if (mode == Server)
219         initServer();
220     else
221         initClient();
222 }
223
224 thread_local SignalProxy *SignalProxy::_current;
225
226
227 void SignalProxy::init()
228 {
229     _heartBeatInterval = 0;
230     _maxHeartBeatCount = 0;
231     _signalRelay = new SignalRelay(this);
232     setHeartBeatInterval(30);
233     setMaxHeartBeatCount(2);
234     _secure = false;
235     _current = this;
236     updateSecureState();
237 }
238
239
240 void SignalProxy::initServer()
241 {
242 }
243
244
245 void SignalProxy::initClient()
246 {
247     attachSlot("__objectRenamed__", this, SLOT(objectRenamed(QByteArray,QString,QString)));
248 }
249
250
251 void SignalProxy::setHeartBeatInterval(int secs)
252 {
253     if (_heartBeatInterval != secs) {
254         _heartBeatInterval = secs;
255         emit heartBeatIntervalChanged(secs);
256     }
257 }
258
259
260 void SignalProxy::setMaxHeartBeatCount(int max)
261 {
262     if (_maxHeartBeatCount != max) {
263         _maxHeartBeatCount = max;
264         emit maxHeartBeatCountChanged(max);
265     }
266 }
267
268
269 bool SignalProxy::addPeer(Peer *peer)
270 {
271     if (!peer)
272         return false;
273
274     if (_peerMap.values().contains(peer))
275         return true;
276
277     if (!peer->isOpen()) {
278         qWarning("SignalProxy: peer needs to be open!");
279         return false;
280     }
281
282     if (proxyMode() == Client) {
283         if (!_peerMap.isEmpty()) {
284             qWarning("SignalProxy: only one peer allowed in client mode!");
285             return false;
286         }
287         connect(peer, SIGNAL(lagUpdated(int)), SIGNAL(lagUpdated(int)));
288     }
289
290     connect(peer, SIGNAL(disconnected()), SLOT(removePeerBySender()));
291     connect(peer, SIGNAL(secureStateChanged(bool)), SLOT(updateSecureState()));
292
293     if (!peer->parent())
294         peer->setParent(this);
295
296     if (peer->id() < 0) {
297         peer->setId(nextPeerId());
298         peer->setConnectedSince(QDateTime::currentDateTimeUtc());
299     }
300
301     _peerMap[peer->id()] = peer;
302
303     peer->setSignalProxy(this);
304
305     if (peerCount() == 1)
306         emit connected();
307
308     updateSecureState();
309     return true;
310 }
311
312
313 void SignalProxy::removeAllPeers()
314 {
315     Q_ASSERT(proxyMode() == Server || peerCount() <= 1);
316     // wee need to copy that list since we modify it in the loop
317     QList<Peer *> peers = _peerMap.values();
318     for (auto peer : peers) {
319         removePeer(peer);
320     }
321 }
322
323
324 void SignalProxy::removePeer(Peer *peer)
325 {
326     if (!peer) {
327         qWarning() << Q_FUNC_INFO << "Trying to remove a null peer!";
328         return;
329     }
330
331     if (_peerMap.isEmpty()) {
332         qWarning() << "SignalProxy::removePeer(): No peers in use!";
333         return;
334     }
335
336     if (!_peerMap.values().contains(peer)) {
337         qWarning() << "SignalProxy: unknown Peer" << peer;
338         return;
339     }
340
341     disconnect(peer, 0, this, 0);
342     peer->setSignalProxy(0);
343
344     _peerMap.remove(peer->id());
345     emit peerRemoved(peer);
346
347     if (peer->parent() == this)
348         peer->deleteLater();
349
350     updateSecureState();
351
352     if (_peerMap.isEmpty())
353         emit disconnected();
354 }
355
356
357 void SignalProxy::removePeerBySender()
358 {
359     removePeer(qobject_cast<Peer *>(sender()));
360 }
361
362
363 void SignalProxy::renameObject(const SyncableObject *obj, const QString &newname, const QString &oldname)
364 {
365     if (proxyMode() == Client)
366         return;
367
368     const QMetaObject *meta = obj->syncMetaObject();
369     const QByteArray className(meta->className());
370     objectRenamed(className, newname, oldname);
371
372     dispatch(RpcCall("__objectRenamed__", QVariantList() << className << newname << oldname));
373 }
374
375
376 void SignalProxy::objectRenamed(const QByteArray &classname, const QString &newname, const QString &oldname)
377 {
378     if (_syncSlave.contains(classname) && _syncSlave[classname].contains(oldname) && oldname != newname) {
379         SyncableObject *obj = _syncSlave[classname][newname] = _syncSlave[classname].take(oldname);
380         requestInit(obj);
381     }
382 }
383
384
385 const QMetaObject *SignalProxy::metaObject(const QObject *obj)
386 {
387     if (const SyncableObject *syncObject = qobject_cast<const SyncableObject *>(obj))
388         return syncObject->syncMetaObject();
389     else
390         return obj->metaObject();
391 }
392
393
394 SignalProxy::ExtendedMetaObject *SignalProxy::extendedMetaObject(const QMetaObject *meta) const
395 {
396     if (_extendedMetaObjects.contains(meta))
397         return _extendedMetaObjects[meta];
398     else
399         return 0;
400 }
401
402
403 SignalProxy::ExtendedMetaObject *SignalProxy::createExtendedMetaObject(const QMetaObject *meta, bool checkConflicts)
404 {
405     if (!_extendedMetaObjects.contains(meta)) {
406         _extendedMetaObjects[meta] = new ExtendedMetaObject(meta, checkConflicts);
407     }
408     return _extendedMetaObjects[meta];
409 }
410
411
412 bool SignalProxy::attachSignal(QObject *sender, const char *signal, const QByteArray &sigName)
413 {
414     const QMetaObject *meta = sender->metaObject();
415     QByteArray sig(meta->normalizedSignature(signal).mid(1));
416     int methodId = meta->indexOfMethod(sig.constData());
417     if (methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
418         qWarning() << "SignalProxy::attachSignal(): No such signal" << signal;
419         return false;
420     }
421
422     createExtendedMetaObject(meta);
423     _signalRelay->attachSignal(sender, methodId, sigName);
424
425     disconnect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
426     connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
427     return true;
428 }
429
430
431 bool SignalProxy::attachSlot(const QByteArray &sigName, QObject *recv, const char *slot)
432 {
433     const QMetaObject *meta = recv->metaObject();
434     int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
435     if (methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
436         qWarning() << "SignalProxy::attachSlot(): No such slot" << slot;
437         return false;
438     }
439
440     createExtendedMetaObject(meta);
441
442     QByteArray funcName = QMetaObject::normalizedSignature(sigName.constData());
443     _attachedSlots.insert(funcName, qMakePair(recv, methodId));
444
445     disconnect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
446     connect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
447     return true;
448 }
449
450
451 void SignalProxy::synchronize(SyncableObject *obj)
452 {
453     createExtendedMetaObject(obj, true);
454
455     // attaching as slave to receive sync Calls
456     QByteArray className(obj->syncMetaObject()->className());
457     _syncSlave[className][obj->objectName()] = obj;
458
459     if (proxyMode() == Server) {
460         obj->setInitialized();
461         emit objectInitialized(obj);
462     }
463     else {
464         if (obj->isInitialized())
465             emit objectInitialized(obj);
466         else
467             requestInit(obj);
468     }
469
470     obj->synchronize(this);
471 }
472
473
474 void SignalProxy::detachObject(QObject *obj)
475 {
476     detachSignals(obj);
477     detachSlots(obj);
478 }
479
480
481 void SignalProxy::detachSignals(QObject *sender)
482 {
483     _signalRelay->detachSignal(sender);
484 }
485
486
487 void SignalProxy::detachSlots(QObject *receiver)
488 {
489     SlotHash::iterator slotIter = _attachedSlots.begin();
490     while (slotIter != _attachedSlots.end()) {
491         if (slotIter.value().first == receiver) {
492             slotIter = _attachedSlots.erase(slotIter);
493         }
494         else
495             ++slotIter;
496     }
497 }
498
499
500 void SignalProxy::stopSynchronize(SyncableObject *obj)
501 {
502     // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
503     // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
504     QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
505     while (classIter != _syncSlave.end()) {
506         if (classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
507             classIter->remove(obj->objectName());
508             break;
509         }
510         ++classIter;
511     }
512     obj->stopSynchronize(this);
513 }
514
515
516 template<class T>
517 void SignalProxy::dispatch(const T &protoMessage)
518 {
519     for (auto peer : _peerMap.values()) {
520         _targetPeer = peer;
521
522         if (peer->isOpen())
523             peer->dispatch(protoMessage);
524         else
525             QCoreApplication::postEvent(this, new ::RemovePeerEvent(peer));
526     }
527     _targetPeer = nullptr;
528 }
529
530
531 template<class T>
532 void SignalProxy::dispatch(Peer *peer, const T &protoMessage)
533 {
534     _targetPeer = peer;
535
536     if (peer && peer->isOpen())
537         peer->dispatch(protoMessage);
538     else
539         QCoreApplication::postEvent(this, new ::RemovePeerEvent(peer));
540
541     _targetPeer = nullptr;
542 }
543
544
545 void SignalProxy::handle(Peer *peer, const SyncMessage &syncMessage)
546 {
547     if (!_syncSlave.contains(syncMessage.className) || !_syncSlave[syncMessage.className].contains(syncMessage.objectName)) {
548         qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(syncMessage.className, syncMessage.slotName, syncMessage.objectName)
549                    << syncMessage.params;
550         return;
551     }
552
553     SyncableObject *receiver = _syncSlave[syncMessage.className][syncMessage.objectName];
554     ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
555     if (!eMeta->slotMap().contains(syncMessage.slotName)) {
556         qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(syncMessage.className, syncMessage.slotName, syncMessage.objectName)
557                    << syncMessage.params;
558         return;
559     }
560
561     int slotId = eMeta->slotMap()[syncMessage.slotName];
562     if (proxyMode() != eMeta->receiverMode(slotId)) {
563         qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed. Wrong ProxyMode!", eMeta->methodName(slotId).constData());
564         return;
565     }
566
567     // We can no longer construct a QVariant from QMetaType::Void
568     QVariant returnValue;
569     int returnType = eMeta->returnType(slotId);
570     if (returnType != QMetaType::Void)
571         returnValue = QVariant(static_cast<QVariant::Type>(returnType));
572
573     if (!invokeSlot(receiver, slotId, syncMessage.params, returnValue, peer)) {
574         qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", eMeta->methodName(slotId).constData());
575         return;
576     }
577
578     if (returnValue.type() != QVariant::Invalid && eMeta->receiveMap().contains(slotId)) {
579         int receiverId = eMeta->receiveMap()[slotId];
580         QVariantList returnParams;
581         if (eMeta->argTypes(receiverId).count() > 1)
582             returnParams << syncMessage.params;
583         returnParams << returnValue;
584         _targetPeer = peer;
585         peer->dispatch(SyncMessage(syncMessage.className, syncMessage.objectName, eMeta->methodName(receiverId), returnParams));
586         _targetPeer = nullptr;
587     }
588
589     // send emit update signal
590     invokeSlot(receiver, eMeta->updatedRemotelyId());
591 }
592
593
594 void SignalProxy::handle(Peer *peer, const InitRequest &initRequest)
595 {
596    if (!_syncSlave.contains(initRequest.className)) {
597         qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:"
598                    << initRequest.className;
599         return;
600     }
601
602     if (!_syncSlave[initRequest.className].contains(initRequest.objectName)) {
603         qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:"
604                    << initRequest.className << initRequest.objectName;
605         return;
606     }
607
608     SyncableObject *obj = _syncSlave[initRequest.className][initRequest.objectName];
609     _targetPeer = peer;
610     peer->dispatch(InitData(initRequest.className, initRequest.objectName, initData(obj)));
611     _targetPeer = nullptr;
612 }
613
614
615 void SignalProxy::handle(Peer *peer, const InitData &initData)
616 {
617     Q_UNUSED(peer)
618
619     if (!_syncSlave.contains(initData.className)) {
620         qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:"
621                    << initData.className;
622         return;
623     }
624
625     if (!_syncSlave[initData.className].contains(initData.objectName)) {
626         qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:"
627                    << initData.className << initData.objectName;
628         return;
629     }
630
631     SyncableObject *obj = _syncSlave[initData.className][initData.objectName];
632     setInitData(obj, initData.initData);
633 }
634
635
636 void SignalProxy::handle(Peer *peer, const RpcCall &rpcCall)
637 {
638     QObject *receiver;
639     int methodId;
640     SlotHash::const_iterator slot = _attachedSlots.constFind(rpcCall.slotName);
641     while (slot != _attachedSlots.constEnd() && slot.key() == rpcCall.slotName) {
642         receiver = (*slot).first;
643         methodId = (*slot).second;
644         if (!invokeSlot(receiver, methodId, rpcCall.params, peer)) {
645             ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
646             qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", eMeta->methodName(methodId).constData());
647         }
648         ++slot;
649     }
650 }
651
652
653 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, QVariant &returnValue, Peer *peer)
654 {
655     ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
656     const QList<int> args = eMeta->argTypes(methodId);
657     const int numArgs = params.count() < args.count()
658                         ? params.count()
659                         : args.count();
660
661     if (eMeta->minArgCount(methodId) > params.count()) {
662         qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << eMeta->methodName(methodId);
663         return false;
664     }
665
666     void *_a[] = { 0,           // return type...
667                    0, 0, 0, 0, 0, // and 10 args - that's the max size qt can handle with signals and slots
668                    0, 0, 0, 0, 0 };
669
670     // check for argument compatibility and build params array
671     for (int i = 0; i < numArgs; i++) {
672         if (!params[i].isValid()) {
673 #if QT_VERSION >= 0x050000
674             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());
675 #else
676             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());
677 #endif
678             qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
679             return false;
680         }
681         if (args[i] != QMetaType::type(params[i].typeName())) {
682             qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << eMeta->methodName(methodId);
683             return false;
684         }
685
686         _a[i+1] = const_cast<void *>(params[i].constData());
687     }
688
689     if (returnValue.type() != QVariant::Invalid)
690         _a[0] = const_cast<void *>(returnValue.constData());
691
692     Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
693                               ? Qt::DirectConnection
694                               : Qt::QueuedConnection;
695
696     if (type == Qt::DirectConnection) {
697         _sourcePeer = peer;
698         auto result = receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
699         _sourcePeer = nullptr;
700         return result;
701     } else {
702         qWarning() << "Queued Connections are not implemented yet";
703         // note to self: qmetaobject.cpp:990 ff
704         return false;
705     }
706 }
707
708
709 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, Peer *peer)
710 {
711     QVariant ret;
712     return invokeSlot(receiver, methodId, params, ret, peer);
713 }
714
715
716 void SignalProxy::requestInit(SyncableObject *obj)
717 {
718     if (proxyMode() == Server || obj->isInitialized())
719         return;
720
721     dispatch(InitRequest(obj->syncMetaObject()->className(), obj->objectName()));
722 }
723
724
725 QVariantMap SignalProxy::initData(SyncableObject *obj) const
726 {
727     return obj->toVariantMap();
728 }
729
730
731 void SignalProxy::setInitData(SyncableObject *obj, const QVariantMap &properties)
732 {
733     if (obj->isInitialized())
734         return;
735     obj->fromVariantMap(properties);
736     obj->setInitialized();
737     emit objectInitialized(obj);
738     invokeSlot(obj, extendedMetaObject(obj)->updatedRemotelyId());
739 }
740
741
742 void SignalProxy::customEvent(QEvent *event)
743 {
744     switch ((int)event->type()) {
745     case RemovePeerEvent: {
746         ::RemovePeerEvent *e = static_cast< ::RemovePeerEvent *>(event);
747         removePeer(e->peer);
748         event->accept();
749         break;
750     }
751
752     default:
753         qWarning() << Q_FUNC_INFO << "Received unknown custom event:" << event->type();
754         return;
755     }
756 }
757
758
759 void SignalProxy::sync_call__(const SyncableObject *obj, SignalProxy::ProxyMode modeType, const char *funcname, va_list ap)
760 {
761     // qDebug() << obj << modeType << "(" << _proxyMode << ")" << funcname;
762     if (modeType != _proxyMode)
763         return;
764
765     ExtendedMetaObject *eMeta = extendedMetaObject(obj);
766
767     QVariantList params;
768
769     const QList<int> &argTypes = eMeta->argTypes(eMeta->methodId(QByteArray(funcname)));
770
771     for (int i = 0; i < argTypes.size(); i++) {
772         if (argTypes[i] == 0) {
773             qWarning() << Q_FUNC_INFO << "received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(eMeta->metaObject()->className()).arg(funcname);
774             qWarning() << "        - make sure all your data types are known by the Qt MetaSystem";
775             return;
776         }
777         params << QVariant(argTypes[i], va_arg(ap, void *));
778     }
779
780     if (_restrictMessageTarget) {
781         for (auto peer : _restrictedTargets) {
782             if (peer != nullptr)
783                 dispatch(peer, SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
784         }
785     } else
786         dispatch(SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
787 }
788
789
790 void SignalProxy::disconnectDevice(QIODevice *dev, const QString &reason)
791 {
792     if (!reason.isEmpty())
793         qWarning() << qPrintable(reason);
794     QAbstractSocket *sock  = qobject_cast<QAbstractSocket *>(dev);
795     if (sock)
796         qWarning() << qPrintable(tr("Disconnecting")) << qPrintable(sock->peerAddress().toString());
797     dev->close();
798 }
799
800
801 void SignalProxy::dumpProxyStats()
802 {
803     QString mode;
804     if (proxyMode() == Server)
805         mode = "Server";
806     else
807         mode = "Client";
808
809     int slaveCount = 0;
810     foreach(ObjectId oid, _syncSlave.values())
811     slaveCount += oid.count();
812
813     qDebug() << this;
814     qDebug() << "              Proxy Mode:" << mode;
815     qDebug() << "          attached Slots:" << _attachedSlots.count();
816     qDebug() << " number of synced Slaves:" << slaveCount;
817     qDebug() << "number of Classes cached:" << _extendedMetaObjects.count();
818 }
819
820
821 void SignalProxy::updateSecureState()
822 {
823     bool wasSecure = _secure;
824
825     _secure = !_peerMap.isEmpty();
826     for (auto peer :  _peerMap.values()) {
827         _secure &= peer->isSecure();
828     }
829
830     if (wasSecure != _secure)
831         emit secureStateChanged(_secure);
832 }
833
834 QVariantList SignalProxy::peerData() {
835     QVariantList result;
836     for (auto peer : _peerMap.values()) {
837         QVariantMap data;
838         data["id"] = peer->id();
839         data["clientVersion"] = peer->clientVersion();
840         // We explicitly rename this, as, due to the Debian reproducability changes, buildDate isn’t actually the build
841         // date anymore, but on newer clients the date of the last git commit
842         data["clientVersionDate"] = peer->buildDate();
843         data["remoteAddress"] = peer->address();
844         data["connectedSince"] = peer->connectedSince();
845         data["secure"] = peer->isSecure();
846         result << data;
847     }
848     return result;
849 }
850
851 Peer *SignalProxy::peerById(int peerId) {
852     return _peerMap[peerId];
853 }
854
855 void SignalProxy::restrictTargetPeers(QSet<Peer*> peers, std::function<void()> closure)
856 {
857     auto previousRestrictMessageTarget = _restrictMessageTarget;
858     auto previousRestrictedTargets = _restrictedTargets;
859     _restrictMessageTarget = true;
860     _restrictedTargets = peers;
861
862     closure();
863
864     _restrictMessageTarget = previousRestrictMessageTarget;
865     _restrictedTargets = previousRestrictedTargets;
866 }
867
868 Peer *SignalProxy::sourcePeer() {
869     return _sourcePeer;
870 }
871
872 void SignalProxy::setSourcePeer(Peer *sourcePeer) {
873     _sourcePeer = sourcePeer;
874 }
875
876 Peer *SignalProxy::targetPeer() {
877     return _targetPeer;
878 }
879
880 void SignalProxy::setTargetPeer(Peer *targetPeer) {
881     _targetPeer = targetPeer;
882 }
883
884 // ==================================================
885 //  ExtendedMetaObject
886 // ==================================================
887 SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta, bool checkConflicts)
888     : _meta(meta),
889     _updatedRemotelyId(_meta->indexOfSignal("updatedRemotely()"))
890 {
891     for (int i = 0; i < _meta->methodCount(); i++) {
892         if (_meta->method(i).methodType() != QMetaMethod::Slot)
893             continue;
894
895 #if QT_VERSION >= 0x050000
896         if (_meta->method(i).methodSignature().contains('*'))
897 #else
898         if (QByteArray(_meta->method(i).signature()).contains('*'))
899 #endif
900             continue;  // skip methods with ptr params
901
902         QByteArray method = methodName(_meta->method(i));
903         if (method.startsWith("init"))
904             continue;  // skip initializers
905
906         if (_methodIds.contains(method)) {
907             /* funny... moc creates for methods containing default parameters multiple metaMethod with separate methodIds.
908                we don't care... we just need the full fledged version
909              */
910             const QMetaMethod &current = _meta->method(_methodIds[method]);
911             const QMetaMethod &candidate = _meta->method(i);
912             if (current.parameterTypes().count() > candidate.parameterTypes().count()) {
913                 int minCount = candidate.parameterTypes().count();
914                 QList<QByteArray> commonParams = current.parameterTypes().mid(0, minCount);
915                 if (commonParams == candidate.parameterTypes())
916                     continue;  // we already got the full featured version
917             }
918             else {
919                 int minCount = current.parameterTypes().count();
920                 QList<QByteArray> commonParams = candidate.parameterTypes().mid(0, minCount);
921                 if (commonParams == current.parameterTypes()) {
922                     _methodIds[method] = i; // use the new one
923                     continue;
924                 }
925             }
926             if (checkConflicts) {
927                 qWarning() << "class" << meta->className() << "contains overloaded methods which is currently not supported!";
928 #if QT_VERSION >= 0x050000
929                 qWarning() << " - " << _meta->method(i).methodSignature() << "conflicts with" << _meta->method(_methodIds[method]).methodSignature();
930 #else
931                 qWarning() << " - " << _meta->method(i).signature() << "conflicts with" << _meta->method(_methodIds[method]).signature();
932 #endif
933             }
934             continue;
935         }
936         _methodIds[method] = i;
937     }
938 }
939
940
941 const SignalProxy::ExtendedMetaObject::MethodDescriptor &SignalProxy::ExtendedMetaObject::methodDescriptor(int methodId)
942 {
943     if (!_methods.contains(methodId)) {
944         _methods[methodId] = MethodDescriptor(_meta->method(methodId));
945     }
946     return _methods[methodId];
947 }
948
949
950 const QHash<int, int> &SignalProxy::ExtendedMetaObject::receiveMap()
951 {
952     if (_receiveMap.isEmpty()) {
953         QHash<int, int> receiveMap;
954
955         QMetaMethod requestSlot;
956         QByteArray returnTypeName;
957         QByteArray signature;
958         QByteArray methodName;
959         QByteArray params;
960         int paramsPos;
961         int receiverId;
962         const int methodCount = _meta->methodCount();
963         for (int i = 0; i < methodCount; i++) {
964             requestSlot = _meta->method(i);
965             if (requestSlot.methodType() != QMetaMethod::Slot)
966                 continue;
967
968             returnTypeName = requestSlot.typeName();
969             if (QMetaType::Void == (QMetaType::Type)returnType(i))
970                 continue;
971
972 #if QT_VERSION >= 0x050000
973             signature = requestSlot.methodSignature();
974 #else
975             signature = QByteArray(requestSlot.signature());
976 #endif
977             if (!signature.startsWith("request"))
978                 continue;
979
980             paramsPos = signature.indexOf('(');
981             if (paramsPos == -1)
982                 continue;
983
984             methodName = signature.left(paramsPos);
985             params = signature.mid(paramsPos);
986
987             methodName = methodName.replace("request", "receive");
988             params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
989
990             signature = QMetaObject::normalizedSignature(methodName + params);
991             receiverId = _meta->indexOfSlot(signature);
992
993             if (receiverId == -1) {
994                 signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
995                 receiverId = _meta->indexOfSlot(signature);
996             }
997
998             if (receiverId != -1) {
999                 receiveMap[i] = receiverId;
1000             }
1001         }
1002         _receiveMap = receiveMap;
1003     }
1004     return _receiveMap;
1005 }
1006
1007
1008 QByteArray SignalProxy::ExtendedMetaObject::methodName(const QMetaMethod &method)
1009 {
1010 #if QT_VERSION >= 0x050000
1011     QByteArray sig(method.methodSignature());
1012 #else
1013     QByteArray sig(method.signature());
1014 #endif
1015     return sig.left(sig.indexOf("("));
1016 }
1017
1018
1019 QString SignalProxy::ExtendedMetaObject::methodBaseName(const QMetaMethod &method)
1020 {
1021 #if QT_VERSION >= 0x050000
1022     QString methodname = QString(method.methodSignature()).section("(", 0, 0);
1023 #else
1024     QString methodname = QString(method.signature()).section("(", 0, 0);
1025 #endif
1026
1027     // determine where we have to chop:
1028     int upperCharPos;
1029     if (method.methodType() == QMetaMethod::Slot) {
1030         // we take evertyhing from the first uppercase char if it's slot
1031         upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
1032         if (upperCharPos == -1)
1033             return QString();
1034         methodname = methodname.mid(upperCharPos);
1035     }
1036     else {
1037         // and if it's a signal we discard everything from the last uppercase char
1038         upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
1039         if (upperCharPos == -1)
1040             return QString();
1041         methodname = methodname.left(upperCharPos);
1042     }
1043
1044     methodname[0] = methodname[0].toUpper();
1045
1046     return methodname;
1047 }
1048
1049
1050 SignalProxy::ExtendedMetaObject::MethodDescriptor::MethodDescriptor(const QMetaMethod &method)
1051     : _methodName(SignalProxy::ExtendedMetaObject::methodName(method)),
1052     _returnType(QMetaType::type(method.typeName()))
1053 {
1054     // determine argTypes
1055     QList<QByteArray> paramTypes = method.parameterTypes();
1056     QList<int> argTypes;
1057     for (int i = 0; i < paramTypes.count(); i++) {
1058         argTypes.append(QMetaType::type(paramTypes[i]));
1059     }
1060     _argTypes = argTypes;
1061
1062     // determine minArgCount
1063 #if QT_VERSION >= 0x050000
1064     QString signature(method.methodSignature());
1065 #else
1066     QString signature(method.signature());
1067 #endif
1068     _minArgCount = method.parameterTypes().count() - signature.count("=");
1069
1070     _receiverMode = (_methodName.startsWith("request"))
1071                     ? SignalProxy::Server
1072                     : SignalProxy::Client;
1073 }