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