Strip newlines from 'reason' fields in settings
[quassel.git] / src / common / signalproxy.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2012 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 "signalproxy.h"
22
23 #include <QObject>
24 #include <QIODevice>
25 #include <QAbstractSocket>
26 #include <QHostAddress>
27 #include <QHash>
28 #include <QMultiHash>
29 #include <QList>
30 #include <QSet>
31 #include <QDebug>
32 #include <QMetaMethod>
33 #include <QMetaProperty>
34 #include <QRegExp>
35 #ifdef HAVE_SSL
36 #include <QSslSocket>
37 #endif
38 #include <QThread>
39 #include <QTime>
40 #include <QEvent>
41 #include <QCoreApplication>
42
43 #include "syncableobject.h"
44 #include "util.h"
45
46 // ==================================================
47 //  PeerSignalEvent
48 // ==================================================
49 class PeerSignalEvent : public QEvent
50 {
51 public:
52     PeerSignalEvent(SignalProxy *sender, SignalProxy::RequestType requestType, const QVariantList &params) : QEvent(QEvent::Type(SignalProxy::PeerSignal)), sender(sender), requestType(requestType), params(params) {}
53     SignalProxy *sender;
54     SignalProxy::RequestType requestType;
55     QVariantList params;
56 };
57
58
59 class RemovePeerEvent : public QEvent
60 {
61 public:
62     RemovePeerEvent(QObject *peer) : QEvent(QEvent::Type(SignalProxy::RemovePeer)), peer(peer) {}
63     QObject *peer;
64 };
65
66
67 // ==================================================
68 //  SignalRelay
69 // ==================================================
70 class SignalProxy::SignalRelay : public QObject
71 {
72 /* Q_OBJECT is not necessary or even allowed, because we implement
73    qt_metacall ourselves (and don't use any other features of the meta
74    object system)
75 */
76 public:
77     SignalRelay(SignalProxy *parent) : QObject(parent), _proxy(parent) {}
78     inline SignalProxy *proxy() const { return _proxy; }
79
80     int qt_metacall(QMetaObject::Call _c, int _id, void **_a);
81
82     void attachSignal(QObject *sender, int signalId, const QByteArray &funcName);
83     void detachSignal(QObject *sender, int signalId = -1);
84
85 private:
86     struct Signal {
87         QObject *sender;
88         int signalId;
89         QByteArray signature;
90         Signal(QObject *sender, int sigId, const QByteArray &signature) : sender(sender), signalId(sigId), signature(signature) {}
91         Signal() : sender(0), signalId(-1) {}
92     };
93
94     SignalProxy *_proxy;
95     QHash<int, Signal> _slots;
96 };
97
98
99 void SignalProxy::SignalRelay::attachSignal(QObject *sender, int signalId, const QByteArray &funcName)
100 {
101     // we ride without safetybelts here... all checking for valid method etc pp has to be done by the caller
102     // all connected methodIds are offset by the standard methodCount of QObject
103     int slotId;
104     for (int i = 0;; i++) {
105         if (!_slots.contains(i)) {
106             slotId = i;
107             break;
108         }
109     }
110
111     QByteArray fn;
112     if (!funcName.isEmpty()) {
113         fn = QMetaObject::normalizedSignature(funcName);
114     }
115     else {
116         fn = SIGNAL(fakeMethodSignature());
117         fn = fn.replace("fakeMethodSignature()", sender->metaObject()->method(signalId).signature());
118     }
119
120     _slots[slotId] = Signal(sender, signalId, fn);
121
122     QMetaObject::connect(sender, signalId, this, QObject::staticMetaObject.methodCount() + slotId);
123 }
124
125
126 void SignalProxy::SignalRelay::detachSignal(QObject *sender, int signalId)
127 {
128     QHash<int, Signal>::iterator slotIter = _slots.begin();
129     while (slotIter != _slots.end()) {
130         if (slotIter->sender == sender && (signalId == -1 || slotIter->signalId == signalId)) {
131             slotIter = _slots.erase(slotIter);
132             if (signalId != -1)
133                 break;
134         }
135         else {
136             slotIter++;
137         }
138     }
139 }
140
141
142 int SignalProxy::SignalRelay::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
143 {
144     _id = QObject::qt_metacall(_c, _id, _a);
145     if (_id < 0)
146         return _id;
147
148     if (_c == QMetaObject::InvokeMetaMethod) {
149         if (_slots.contains(_id)) {
150             QObject *caller = sender();
151
152             SignalProxy::ExtendedMetaObject *eMeta = proxy()->extendedMetaObject(caller->metaObject());
153             Q_ASSERT(eMeta);
154
155             const Signal &signal = _slots[_id];
156
157             QVariantList params;
158             params << signal.signature;
159
160             const QList<int> &argTypes = eMeta->argTypes(signal.signalId);
161             for (int i = 0; i < argTypes.size(); i++) {
162                 if (argTypes[i] == 0) {
163                     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());
164                     qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
165                     return _id;
166                 }
167                 params << QVariant(argTypes[i], _a[i+1]);
168             }
169
170             proxy()->dispatchSignal(SignalProxy::RpcCall, params);
171         }
172         _id -= _slots.count();
173     }
174     return _id;
175 }
176
177
178 // ==================================================
179 //  Peers
180 // ==================================================
181 void SignalProxy::IODevicePeer::dispatchSignal(const RequestType &requestType, const QVariantList &params)
182 {
183     QVariantList packedFunc;
184     packedFunc << (qint16)requestType
185                << params;
186     dispatchPackedFunc(QVariant(packedFunc));
187 }
188
189
190 bool SignalProxy::IODevicePeer::isSecure() const
191 {
192 #ifdef HAVE_SSL
193     QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_device);
194     if (sslSocket)
195         return sslSocket->isEncrypted() || sslSocket->localAddress() == QHostAddress::LocalHost || sslSocket->localAddress() == QHostAddress::LocalHostIPv6;
196 #endif
197
198     QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(_device);
199     if (socket)
200         return socket->localAddress() == QHostAddress::LocalHost || socket->localAddress() == QHostAddress::LocalHostIPv6;
201
202     return false;
203 }
204
205
206 QString SignalProxy::IODevicePeer::address() const
207 {
208     QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(_device);
209     if (socket)
210         return socket->peerAddress().toString();
211     else
212         return QString();
213 }
214
215
216 void SignalProxy::SignalProxyPeer::dispatchSignal(const RequestType &requestType, const QVariantList &params)
217 {
218     Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
219                               ? Qt::DirectConnection
220                               : Qt::QueuedConnection;
221
222     if (type == Qt::DirectConnection) {
223         receiver->receivePeerSignal(sender, requestType, params);
224     }
225     else {
226         QCoreApplication::postEvent(receiver, new PeerSignalEvent(sender, requestType, params));
227     }
228 }
229
230
231 // ==================================================
232 //  SignalProxy
233 // ==================================================
234 SignalProxy::SignalProxy(QObject *parent)
235     : QObject(parent)
236 {
237     setProxyMode(Client);
238     init();
239 }
240
241
242 SignalProxy::SignalProxy(ProxyMode mode, QObject *parent)
243     : QObject(parent)
244 {
245     setProxyMode(mode);
246     init();
247 }
248
249
250 SignalProxy::SignalProxy(ProxyMode mode, QIODevice *device, QObject *parent)
251     : QObject(parent)
252 {
253     setProxyMode(mode);
254     addPeer(device);
255     init();
256 }
257
258
259 SignalProxy::~SignalProxy()
260 {
261     QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
262     while (classIter != _syncSlave.end()) {
263         ObjectId::iterator objIter = classIter->begin();
264         while (objIter != classIter->end()) {
265             SyncableObject *obj = objIter.value();
266             objIter = classIter->erase(objIter);
267             obj->stopSynchronize(this);
268         }
269         classIter++;
270     }
271     _syncSlave.clear();
272
273     removeAllPeers();
274 }
275
276
277 void SignalProxy::setProxyMode(ProxyMode mode)
278 {
279     PeerHash::iterator peer = _peers.begin();
280     while (peer != _peers.end()) {
281         if ((*peer)->type() != AbstractPeer::IODevicePeer) {
282             IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
283             if (ioPeer->isOpen()) {
284                 qWarning() << "SignalProxy: Cannot change proxy mode while connected";
285                 return;
286             }
287         }
288         if ((*peer)->type() != AbstractPeer::SignalProxyPeer) {
289             qWarning() << "SignalProxy: Cannot change proxy mode while connected to another internal SignalProxy";
290             return;
291         }
292         peer++;
293     }
294
295     _proxyMode = mode;
296     if (mode == Server)
297         initServer();
298     else
299         initClient();
300 }
301
302
303 void SignalProxy::init()
304 {
305     _heartBeatInterval = 0;
306     _maxHeartBeatCount = 0;
307     _signalRelay = new SignalRelay(this);
308     connect(&_heartBeatTimer, SIGNAL(timeout()), this, SLOT(sendHeartBeat()));
309     setHeartBeatInterval(30);
310     setMaxHeartBeatCount(2);
311     _heartBeatTimer.start();
312     _secure = false;
313     updateSecureState();
314 }
315
316
317 void SignalProxy::initServer()
318 {
319 }
320
321
322 void SignalProxy::initClient()
323 {
324     attachSlot("__objectRenamed__", this, SLOT(objectRenamed(QByteArray, QString, QString)));
325 }
326
327
328 bool SignalProxy::addPeer(QIODevice *iodev)
329 {
330     if (!iodev)
331         return false;
332
333     if (_peers.contains(iodev))
334         return true;
335
336     if (proxyMode() == Client && !_peers.isEmpty()) {
337         qWarning("SignalProxy: only one peer allowed in client mode!");
338         return false;
339     }
340
341     if (!iodev->isOpen()) {
342         qWarning("SignalProxy::addPeer(QIODevice *iodev): iodev needs to be open!");
343         return false;
344     }
345
346     connect(iodev, SIGNAL(disconnected()), this, SLOT(removePeerBySender()));
347     connect(iodev, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
348
349 #ifdef HAVE_SSL
350     QSslSocket *sslSocket = qobject_cast<QSslSocket *>(iodev);
351     if (sslSocket) {
352         connect(iodev, SIGNAL(encrypted()), this, SLOT(updateSecureState()));
353     }
354 #endif
355
356     if (!iodev->parent())
357         iodev->setParent(this);
358
359     _peers[iodev] = new IODevicePeer(iodev, iodev->property("UseCompression").toBool());
360
361     if (_peers.count() == 1)
362         emit connected();
363
364     updateSecureState();
365     return true;
366 }
367
368
369 void SignalProxy::setHeartBeatInterval(int secs)
370 {
371     if (secs != _heartBeatInterval) {
372         _heartBeatInterval = secs;
373         _heartBeatTimer.setInterval(secs * 1000);
374     }
375 }
376
377
378 void SignalProxy::setMaxHeartBeatCount(int max)
379 {
380     _maxHeartBeatCount = max;
381 }
382
383
384 bool SignalProxy::addPeer(SignalProxy *proxy)
385 {
386     if (!proxy)
387         return false;
388
389     if (proxyMode() == proxy->proxyMode()) {
390         qWarning() << "SignalProxy::addPeer(): adding a SignalProxy as peer requires one proxy to be server and one client!";
391         return false;
392     }
393
394     if (_peers.contains(proxy)) {
395         return true;
396     }
397
398     if (proxyMode() == Client && !_peers.isEmpty()) {
399         qWarning("SignalProxy: only one peer allowed in client mode!");
400         return false;
401     }
402
403     _peers[proxy] = new SignalProxyPeer(this, proxy);
404     proxy->addPeer(this);
405
406     if (_peers.count() == 1)
407         emit connected();
408
409     updateSecureState();
410     return true;
411 }
412
413
414 void SignalProxy::removeAllPeers()
415 {
416     Q_ASSERT(proxyMode() == Server || _peers.count() <= 1);
417     // wee need to copy that list since we modify it in the loop
418     QList<QObject *> peers = _peers.keys();
419     foreach(QObject *peer, peers) {
420         removePeer(peer);
421     }
422 }
423
424
425 void SignalProxy::removePeer(QObject *dev)
426 {
427     if (_peers.isEmpty()) {
428         qWarning() << "SignalProxy::removePeer(): No peers in use!";
429         return;
430     }
431
432     Q_ASSERT(dev);
433     if (!_peers.contains(dev)) {
434         qWarning() << "SignalProxy: unknown Peer" << dev;
435         return;
436     }
437
438     AbstractPeer *peer = _peers[dev];
439     _peers.remove(dev);
440
441     disconnect(dev, 0, this, 0);
442     if (peer->type() == AbstractPeer::IODevicePeer)
443         emit peerRemoved(static_cast<QIODevice *>(dev));
444
445     if (peer->type() == AbstractPeer::SignalProxyPeer) {
446         SignalProxy *proxy = static_cast<SignalProxy *>(dev);
447         if (proxy->_peers.contains(this))
448             proxy->removePeer(this);
449     }
450
451     if (dev->parent() == this)
452         dev->deleteLater();
453
454     delete peer;
455
456     updateSecureState();
457
458     if (_peers.isEmpty())
459         emit disconnected();
460 }
461
462
463 void SignalProxy::removePeerBySender()
464 {
465     removePeer(sender());
466 }
467
468
469 void SignalProxy::renameObject(const SyncableObject *obj, const QString &newname, const QString &oldname)
470 {
471     if (proxyMode() == Client)
472         return;
473
474     const QMetaObject *meta = obj->syncMetaObject();
475     const QByteArray className(meta->className());
476     objectRenamed(className, newname, oldname);
477
478     QVariantList params;
479     params << "__objectRenamed__" << className << newname << oldname;
480     dispatchSignal(RpcCall, params);
481 }
482
483
484 void SignalProxy::objectRenamed(const QByteArray &classname, const QString &newname, const QString &oldname)
485 {
486     if (_syncSlave.contains(classname) && _syncSlave[classname].contains(oldname) && oldname != newname) {
487         SyncableObject *obj = _syncSlave[classname][newname] = _syncSlave[classname].take(oldname);
488         requestInit(obj);
489     }
490 }
491
492
493 const QMetaObject *SignalProxy::metaObject(const QObject *obj)
494 {
495     if (const SyncableObject *syncObject = qobject_cast<const SyncableObject *>(obj))
496         return syncObject->syncMetaObject();
497     else
498         return obj->metaObject();
499 }
500
501
502 SignalProxy::ExtendedMetaObject *SignalProxy::extendedMetaObject(const QMetaObject *meta) const
503 {
504     if (_extendedMetaObjects.contains(meta))
505         return _extendedMetaObjects[meta];
506     else
507         return 0;
508 }
509
510
511 SignalProxy::ExtendedMetaObject *SignalProxy::createExtendedMetaObject(const QMetaObject *meta, bool checkConflicts)
512 {
513     if (!_extendedMetaObjects.contains(meta)) {
514         _extendedMetaObjects[meta] = new ExtendedMetaObject(meta, checkConflicts);
515     }
516     return _extendedMetaObjects[meta];
517 }
518
519
520 bool SignalProxy::attachSignal(QObject *sender, const char *signal, const QByteArray &sigName)
521 {
522     const QMetaObject *meta = sender->metaObject();
523     QByteArray sig(meta->normalizedSignature(signal).mid(1));
524     int methodId = meta->indexOfMethod(sig.constData());
525     if (methodId == -1 || meta->method(methodId).methodType() != QMetaMethod::Signal) {
526         qWarning() << "SignalProxy::attachSignal(): No such signal" << signal;
527         return false;
528     }
529
530     createExtendedMetaObject(meta);
531     _signalRelay->attachSignal(sender, methodId, sigName);
532
533     disconnect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
534     connect(sender, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
535     return true;
536 }
537
538
539 bool SignalProxy::attachSlot(const QByteArray &sigName, QObject *recv, const char *slot)
540 {
541     const QMetaObject *meta = recv->metaObject();
542     int methodId = meta->indexOfMethod(meta->normalizedSignature(slot).mid(1));
543     if (methodId == -1 || meta->method(methodId).methodType() == QMetaMethod::Method) {
544         qWarning() << "SignalProxy::attachSlot(): No such slot" << slot;
545         return false;
546     }
547
548     createExtendedMetaObject(meta);
549
550     QByteArray funcName = QMetaObject::normalizedSignature(sigName.constData());
551     _attachedSlots.insert(funcName, qMakePair(recv, methodId));
552
553     disconnect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
554     connect(recv, SIGNAL(destroyed(QObject *)), this, SLOT(detachObject(QObject *)));
555     return true;
556 }
557
558
559 void SignalProxy::synchronize(SyncableObject *obj)
560 {
561     createExtendedMetaObject(obj, true);
562
563     // attaching as slave to receive sync Calls
564     QByteArray className(obj->syncMetaObject()->className());
565     _syncSlave[className][obj->objectName()] = obj;
566
567     if (proxyMode() == Server) {
568         obj->setInitialized();
569         emit objectInitialized(obj);
570     }
571     else {
572         if (obj->isInitialized())
573             emit objectInitialized(obj);
574         else
575             requestInit(obj);
576     }
577
578     obj->synchronize(this);
579 }
580
581
582 void SignalProxy::detachObject(QObject *obj)
583 {
584     detachSignals(obj);
585     detachSlots(obj);
586 }
587
588
589 void SignalProxy::detachSignals(QObject *sender)
590 {
591     _signalRelay->detachSignal(sender);
592 }
593
594
595 void SignalProxy::detachSlots(QObject *receiver)
596 {
597     SlotHash::iterator slotIter = _attachedSlots.begin();
598     while (slotIter != _attachedSlots.end()) {
599         if (slotIter.value().first == receiver) {
600             slotIter = _attachedSlots.erase(slotIter);
601         }
602         else
603             slotIter++;
604     }
605 }
606
607
608 void SignalProxy::stopSynchronize(SyncableObject *obj)
609 {
610     // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
611     // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
612     QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
613     while (classIter != _syncSlave.end()) {
614         if (classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
615             classIter->remove(obj->objectName());
616             break;
617         }
618         classIter++;
619     }
620     obj->stopSynchronize(this);
621 }
622
623
624 void SignalProxy::dispatchSignal(const RequestType &requestType, const QVariantList &params)
625 {
626     QVariant packedFunc(QVariantList() << (qint16)requestType << params);
627     PeerHash::iterator peer = _peers.begin();
628     while (peer != _peers.end()) {
629         switch ((*peer)->type()) {
630         case AbstractPeer::IODevicePeer:
631         {
632             IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
633             if (ioPeer->isOpen())
634                 ioPeer->dispatchPackedFunc(packedFunc);
635             else
636                 QCoreApplication::postEvent(this, new RemovePeerEvent(peer.key()));
637         }
638         break;
639         case AbstractPeer::SignalProxyPeer:
640             (*peer)->dispatchSignal(requestType, params);
641             break;
642         default:
643             Q_ASSERT(false); // there shouldn't be any peers with wrong / unknown type
644         }
645         peer++;
646     }
647 }
648
649
650 void SignalProxy::receivePackedFunc(AbstractPeer *sender, const QVariant &packedFunc)
651 {
652     QVariantList params(packedFunc.toList());
653
654     if (params.isEmpty()) {
655         qWarning() << "SignalProxy::receivePeerSignal(): received incompatible Data:" << packedFunc;
656         return;
657     }
658
659     RequestType requestType = (RequestType)params.takeFirst().value<int>();
660     receivePeerSignal(sender, requestType, params);
661 }
662
663
664 void SignalProxy::receivePeerSignal(AbstractPeer *sender, const RequestType &requestType, const QVariantList &params)
665 {
666     switch (requestType) {
667     // list all RequestTypes that shouldnot trigger a heartbeat counter reset here
668     case HeartBeatReply:
669         break;
670     default:
671         if (sender->type() == AbstractPeer::IODevicePeer) {
672             IODevicePeer *ioPeer = static_cast<IODevicePeer *>(sender);
673             ioPeer->sentHeartBeats = 0;
674         }
675     }
676
677     // qDebug() << "SignalProxy::receivePeerSignal)" << requestType << params;
678     switch (requestType) {
679     case RpcCall:
680         if (params.empty())
681             qWarning() << "SignalProxy::receivePeerSignal(): received empty RPC-Call";
682         else
683             handleSignal(params);
684         //handleSignal(params.takeFirst().toByteArray(), params);
685         break;
686
687     case Sync:
688         handleSync(sender, params);
689         break;
690
691     case InitRequest:
692         handleInitRequest(sender, params);
693         break;
694
695     case InitData:
696         handleInitData(sender, params);
697         break;
698
699     case HeartBeat:
700         receiveHeartBeat(sender, params);
701         break;
702
703     case HeartBeatReply:
704         receiveHeartBeatReply(sender, params);
705         break;
706
707     default:
708         qWarning() << "SignalProxy::receivePeerSignal(): received undefined CallType" << requestType << params;
709     }
710 }
711
712
713 void SignalProxy::receivePeerSignal(SignalProxy *sender, const RequestType &requestType, const QVariantList &params)
714 {
715     if (!_peers.contains(sender)) {
716         // we output only the pointer value. otherwise Qt would try to pretty print. As the object might already been destroyed, this is not a good idea.
717         qWarning() << "SignalProxy::receivePeerSignal(): received Signal from unknown Proxy" << reinterpret_cast<void *>(sender);
718         return;
719     }
720     receivePeerSignal(_peers[sender], requestType, params);
721 }
722
723
724 void SignalProxy::handleSync(AbstractPeer *sender, QVariantList params)
725 {
726     if (params.count() < 3) {
727         qWarning() << "received invalid Sync call" << params;
728         return;
729     }
730
731     QByteArray className = params.takeFirst().toByteArray();
732     QString objectName = params.takeFirst().toString();
733     QByteArray slot = params.takeFirst().toByteArray();
734
735     if (!_syncSlave.contains(className) || !_syncSlave[className].contains(objectName)) {
736         qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(slot)).arg(objectName)
737                    << params;
738         return;
739     }
740
741     SyncableObject *receiver = _syncSlave[className][objectName];
742     ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
743     if (!eMeta->slotMap().contains(slot)) {
744         qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:").arg(QString(className)).arg(QString(slot)).arg(objectName)
745                    << params;
746         return;
747     }
748
749     int slotId = eMeta->slotMap()[slot];
750     if (proxyMode() != eMeta->receiverMode(slotId)) {
751         qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed. Wrong ProxyMode!", eMeta->methodName(slotId).constData());
752         return;
753     }
754
755     QVariant returnValue((QVariant::Type)eMeta->returnType(slotId));
756     if (!invokeSlot(receiver, slotId, params, returnValue)) {
757         qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", eMeta->methodName(slotId).constData());
758         return;
759     }
760
761     if (returnValue.type() != QVariant::Invalid && eMeta->receiveMap().contains(slotId)) {
762         int receiverId = eMeta->receiveMap()[slotId];
763         QVariantList returnParams;
764         returnParams << className
765                      << objectName
766                      << eMeta->methodName(receiverId);
767         //QByteArray(receiver->metaObject()->method(receiverId).signature());
768         if (eMeta->argTypes(receiverId).count() > 1)
769             returnParams << params;
770         returnParams << returnValue;
771         sender->dispatchSignal(Sync, returnParams);
772     }
773
774     // send emit update signal
775     invokeSlot(receiver, eMeta->updatedRemotelyId());
776 }
777
778
779 void SignalProxy::handleInitRequest(AbstractPeer *sender, const QVariantList &params)
780 {
781     if (params.count() != 2) {
782         qWarning() << "SignalProxy::handleInitRequest() received initRequest with invalid param Count:"
783                    << params;
784         return;
785     }
786
787     QByteArray className(params[0].toByteArray());
788     QString objectName(params[1].toString());
789
790     if (!_syncSlave.contains(className)) {
791         qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:"
792                    << className;
793         return;
794     }
795
796     if (!_syncSlave[className].contains(objectName)) {
797         qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:"
798                    << className << objectName;
799         return;
800     }
801
802     SyncableObject *obj = _syncSlave[className][objectName];
803
804     QVariantList params_;
805     params_ << className
806             << objectName
807             << initData(obj);
808
809     sender->dispatchSignal(InitData, params_);
810 }
811
812
813 void SignalProxy::handleInitData(AbstractPeer *sender, const QVariantList &params)
814 {
815     Q_UNUSED(sender)
816     if (params.count() != 3) {
817         qWarning() << "SignalProxy::handleInitData() received initData with invalid param Count:"
818                    << params;
819         return;
820     }
821
822     QByteArray className(params[0].toByteArray());
823     QString objectName(params[1].toString());
824     QVariantMap propertyMap(params[2].toMap());
825
826     if (!_syncSlave.contains(className)) {
827         qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:"
828                    << className;
829         return;
830     }
831
832     if (!_syncSlave[className].contains(objectName)) {
833         qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:"
834                    << className << objectName;
835         return;
836     }
837
838     SyncableObject *obj = _syncSlave[className][objectName];
839     setInitData(obj, propertyMap);
840 }
841
842
843 //void SignalProxy::handleSignal(const QByteArray &funcName, const QVariantList &params) {
844 void SignalProxy::handleSignal(const QVariantList &data)
845 {
846     QVariantList params = data;
847     QByteArray funcName = params.takeFirst().toByteArray();
848
849     QObject *receiver;
850     int methodId;
851     SlotHash::const_iterator slot = _attachedSlots.constFind(funcName);
852     while (slot != _attachedSlots.constEnd() && slot.key() == funcName) {
853         receiver = (*slot).first;
854         methodId = (*slot).second;
855         if (!invokeSlot(receiver, methodId, params)) {
856             ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
857             qWarning("SignalProxy::handleSignal(): invokeMethod for \"%s\" failed ", eMeta->methodName(methodId).constData());
858         }
859         slot++;
860     }
861 }
862
863
864 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params, QVariant &returnValue)
865 {
866     ExtendedMetaObject *eMeta = extendedMetaObject(receiver);
867     const QList<int> args = eMeta->argTypes(methodId);
868     const int numArgs = params.count() < args.count()
869                         ? params.count()
870                         : args.count();
871
872     if (eMeta->minArgCount(methodId) > params.count()) {
873         qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << eMeta->methodName(methodId);
874         return false;
875     }
876
877     void *_a[] = { 0,           // return type...
878                    0, 0, 0, 0, 0, // and 10 args - that's the max size qt can handle with signals and slots
879                    0, 0, 0, 0, 0 };
880
881     // check for argument compatibility and build params array
882     for (int i = 0; i < numArgs; i++) {
883         if (!params[i].isValid()) {
884             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());
885             qWarning() << "                            - make sure all your data types are known by the Qt MetaSystem";
886             return false;
887         }
888         if (args[i] != QMetaType::type(params[i].typeName())) {
889             qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << eMeta->methodName(methodId);
890             return false;
891         }
892         _a[i+1] = const_cast<void *>(params[i].constData());
893     }
894
895     if (returnValue.type() != QVariant::Invalid)
896         _a[0] = const_cast<void *>(returnValue.constData());
897
898     Qt::ConnectionType type = QThread::currentThread() == receiver->thread()
899                               ? Qt::DirectConnection
900                               : Qt::QueuedConnection;
901
902     if (type == Qt::DirectConnection) {
903         return receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
904     }
905     else {
906         qWarning() << "Queued Connections are not implemented yet";
907         // note to self: qmetaobject.cpp:990 ff
908         return false;
909     }
910 }
911
912
913 bool SignalProxy::invokeSlot(QObject *receiver, int methodId, const QVariantList &params)
914 {
915     QVariant ret;
916     return invokeSlot(receiver, methodId, params, ret);
917 }
918
919
920 void SignalProxy::dataAvailable()
921 {
922     // yet again. it's a private slot. no need for checks.
923     QIODevice *ioDev = qobject_cast<QIODevice *>(sender());
924     Q_ASSERT(_peers.contains(ioDev) && _peers[ioDev]->type() == AbstractPeer::IODevicePeer);
925     IODevicePeer *peer = static_cast<IODevicePeer *>(_peers[ioDev]);
926     QVariant var;
927     while (peer->readData(var))
928         receivePackedFunc(peer, var);
929 }
930
931
932 void SignalProxy::writeDataToDevice(QIODevice *dev, const QVariant &item, bool compressed)
933 {
934     QAbstractSocket *sock  = qobject_cast<QAbstractSocket *>(dev);
935     if (!dev->isOpen() || (sock && sock->state() != QAbstractSocket::ConnectedState)) {
936         qWarning("SignalProxy: Can't call write on a closed device");
937         return;
938     }
939
940     QByteArray block;
941     QDataStream out(&block, QIODevice::WriteOnly);
942     out.setVersion(QDataStream::Qt_4_2);
943     out << (quint32)0;
944
945     if (compressed) {
946         QByteArray rawItem;
947         QDataStream itemStream(&rawItem, QIODevice::WriteOnly);
948
949         itemStream.setVersion(QDataStream::Qt_4_2);
950         itemStream << item;
951
952         rawItem = qCompress(rawItem);
953
954         out << rawItem;
955     }
956     else {
957         out << item;
958     }
959
960     out.device()->seek(0);
961     out << (quint32)(block.size() - sizeof(quint32));
962
963     dev->write(block);
964 }
965
966
967 bool SignalProxy::readDataFromDevice(QIODevice *dev, quint32 &blockSize, QVariant &item, bool compressed)
968 {
969     if (!dev)
970         return false;
971
972     QDataStream in(dev);
973     in.setVersion(QDataStream::Qt_4_2);
974
975     if (blockSize == 0) {
976         if (dev->bytesAvailable() < (int)sizeof(quint32)) return false;
977         in >> blockSize;
978     }
979
980     if (blockSize > 1 << 22) {
981         disconnectDevice(dev, tr("Peer tried to send package larger than max package size!"));
982         return false;
983     }
984
985     if (blockSize == 0) {
986         disconnectDevice(dev, tr("Peer tried to send 0 byte package!"));
987         return false;
988     }
989
990     if (dev->bytesAvailable() < blockSize)
991         return false;
992
993     blockSize = 0;
994
995     if (compressed) {
996         QByteArray rawItem;
997         in >> rawItem;
998
999         int nbytes = rawItem.size();
1000         if (nbytes <= 4) {
1001             const char *data = rawItem.constData();
1002             if (nbytes < 4 || (data[0] != 0 || data[1] != 0 || data[2] != 0 || data[3] != 0)) {
1003                 disconnectDevice(dev, tr("Peer sent corrupted compressed data!"));
1004                 return false;
1005             }
1006         }
1007
1008         rawItem = qUncompress(rawItem);
1009
1010         QDataStream itemStream(&rawItem, QIODevice::ReadOnly);
1011         itemStream.setVersion(QDataStream::Qt_4_2);
1012         itemStream >> item;
1013     }
1014     else {
1015         in >> item;
1016     }
1017
1018     if (!item.isValid()) {
1019         disconnectDevice(dev, tr("Peer sent corrupt data: unable to load QVariant!"));
1020         return false;
1021     }
1022
1023     return true;
1024 }
1025
1026
1027 void SignalProxy::requestInit(SyncableObject *obj)
1028 {
1029     if (proxyMode() == Server || obj->isInitialized())
1030         return;
1031
1032     QVariantList params;
1033     params << obj->syncMetaObject()->className()
1034            << obj->objectName();
1035     dispatchSignal(InitRequest, params);
1036 }
1037
1038
1039 QVariantMap SignalProxy::initData(SyncableObject *obj) const
1040 {
1041     return obj->toVariantMap();
1042 }
1043
1044
1045 void SignalProxy::setInitData(SyncableObject *obj, const QVariantMap &properties)
1046 {
1047     if (obj->isInitialized())
1048         return;
1049     obj->fromVariantMap(properties);
1050     obj->setInitialized();
1051     emit objectInitialized(obj);
1052     invokeSlot(obj, extendedMetaObject(obj)->updatedRemotelyId());
1053 }
1054
1055
1056 void SignalProxy::sendHeartBeat()
1057 {
1058     QVariantList heartBeatParams;
1059     heartBeatParams << QTime::currentTime();
1060     QList<IODevicePeer *> toClose;
1061
1062     PeerHash::iterator peer = _peers.begin();
1063     while (peer != _peers.end()) {
1064         if ((*peer)->type() == AbstractPeer::IODevicePeer) {
1065             IODevicePeer *ioPeer = static_cast<IODevicePeer *>(*peer);
1066             ioPeer->dispatchSignal(SignalProxy::HeartBeat, heartBeatParams);
1067             if (ioPeer->sentHeartBeats > 0) {
1068                 updateLag(ioPeer, ioPeer->sentHeartBeats * _heartBeatTimer.interval());
1069             }
1070             if (maxHeartBeatCount() >= 0 && ioPeer->sentHeartBeats >= maxHeartBeatCount())
1071                 toClose.append(ioPeer);
1072             else
1073                 ioPeer->sentHeartBeats++;
1074         }
1075         ++peer;
1076     }
1077
1078     foreach(IODevicePeer *ioPeer, toClose) {
1079         qWarning() << "SignalProxy: Disconnecting peer:" << ioPeer->address()
1080                    << "(didn't receive a heartbeat for over" << ioPeer->sentHeartBeats *_heartBeatTimer.interval() / 1000 << "seconds)";
1081         ioPeer->close();
1082     }
1083 }
1084
1085
1086 void SignalProxy::receiveHeartBeat(AbstractPeer *peer, const QVariantList &params)
1087 {
1088     peer->dispatchSignal(SignalProxy::HeartBeatReply, params);
1089 }
1090
1091
1092 void SignalProxy::receiveHeartBeatReply(AbstractPeer *peer, const QVariantList &params)
1093 {
1094     if (peer->type() != AbstractPeer::IODevicePeer) {
1095         qWarning() << "SignalProxy::receiveHeartBeatReply: received heart beat from a non IODevicePeer!";
1096         return;
1097     }
1098
1099     IODevicePeer *ioPeer = static_cast<IODevicePeer *>(peer);
1100     ioPeer->sentHeartBeats = 0;
1101
1102     if (params.isEmpty()) {
1103         qWarning() << "SignalProxy: received heart beat reply with less params then sent from:" << ioPeer->address();
1104         return;
1105     }
1106
1107     QTime sendTime = params[0].value<QTime>();
1108     updateLag(ioPeer, sendTime.msecsTo(QTime::currentTime()) / 2);
1109 }
1110
1111
1112 void SignalProxy::customEvent(QEvent *event)
1113 {
1114     switch (+event->type()) {
1115     case PeerSignal:
1116     {
1117         PeerSignalEvent *e = static_cast<PeerSignalEvent *>(event);
1118         receivePeerSignal(e->sender, e->requestType, e->params);
1119     }
1120         event->accept();
1121         break;
1122     case RemovePeer:
1123     {
1124         RemovePeerEvent *e = static_cast<RemovePeerEvent *>(event);
1125         removePeer(e->peer);
1126     }
1127         event->accept();
1128     default:
1129         return;
1130     }
1131 }
1132
1133
1134 void SignalProxy::sync_call__(const SyncableObject *obj, SignalProxy::ProxyMode modeType, const char *funcname, va_list ap)
1135 {
1136     // qDebug() << obj << modeType << "(" << _proxyMode << ")" << funcname;
1137     if (modeType != _proxyMode)
1138         return;
1139
1140     ExtendedMetaObject *eMeta = extendedMetaObject(obj);
1141
1142     QVariantList params;
1143     params << eMeta->metaObject()->className()
1144            << obj->objectName()
1145            << QByteArray(funcname);
1146
1147     const QList<int> &argTypes = eMeta->argTypes(eMeta->methodId(QByteArray(funcname)));
1148
1149     for (int i = 0; i < argTypes.size(); i++) {
1150         if (argTypes[i] == 0) {
1151             qWarning() << Q_FUNC_INFO << "received invalid data for argument number" << i << "of signal" << QString("%1::%2").arg(eMeta->metaObject()->className()).arg(funcname);
1152             qWarning() << "        - make sure all your data types are known by the Qt MetaSystem";
1153             return;
1154         }
1155         params << QVariant(argTypes[i], va_arg(ap, void *));
1156     }
1157
1158     dispatchSignal(Sync, params);
1159 }
1160
1161
1162 void SignalProxy::disconnectDevice(QIODevice *dev, const QString &reason)
1163 {
1164     if (!reason.isEmpty())
1165         qWarning() << qPrintable(reason);
1166     QAbstractSocket *sock  = qobject_cast<QAbstractSocket *>(dev);
1167     if (sock)
1168         qWarning() << qPrintable(tr("Disconnecting")) << qPrintable(sock->peerAddress().toString());
1169     dev->close();
1170 }
1171
1172
1173 void SignalProxy::updateLag(IODevicePeer *peer, int lag)
1174 {
1175     peer->lag = lag;
1176     if (proxyMode() == Client) {
1177         emit lagUpdated(lag);
1178     }
1179 }
1180
1181
1182 void SignalProxy::dumpProxyStats()
1183 {
1184     QString mode;
1185     if (proxyMode() == Server)
1186         mode = "Server";
1187     else
1188         mode = "Client";
1189
1190     int slaveCount = 0;
1191     foreach(ObjectId oid, _syncSlave.values())
1192     slaveCount += oid.count();
1193
1194     qDebug() << this;
1195     qDebug() << "              Proxy Mode:" << mode;
1196     qDebug() << "          attached Slots:" << _attachedSlots.count();
1197     qDebug() << " number of synced Slaves:" << slaveCount;
1198     qDebug() << "number of Classes cached:" << _extendedMetaObjects.count();
1199 }
1200
1201
1202 void SignalProxy::updateSecureState()
1203 {
1204     bool wasSecure = _secure;
1205
1206     _secure = !_peers.isEmpty();
1207     PeerHash::const_iterator peerIter;
1208     for (peerIter = _peers.constBegin(); peerIter != _peers.constEnd(); peerIter++) {
1209         _secure &= (*peerIter)->isSecure();
1210     }
1211
1212     if (wasSecure != _secure)
1213         emit secureStateChanged(_secure);
1214 }
1215
1216
1217 // ==================================================
1218 //  ExtendedMetaObject
1219 // ==================================================
1220 SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject *meta, bool checkConflicts)
1221     : _meta(meta),
1222     _updatedRemotelyId(_meta->indexOfSignal("updatedRemotely()"))
1223 {
1224     for (int i = 0; i < _meta->methodCount(); i++) {
1225         if (_meta->method(i).methodType() != QMetaMethod::Slot)
1226             continue;
1227
1228         if (QByteArray(_meta->method(i).signature()).contains('*'))
1229             continue;  // skip methods with ptr params
1230
1231         QByteArray method = methodName(_meta->method(i));
1232         if (method.startsWith("init"))
1233             continue;  // skip initializers
1234
1235         if (_methodIds.contains(method)) {
1236             /* funny... moc creates for methods containing default parameters multiple metaMethod with separate methodIds.
1237                we don't care... we just need the full fledged version
1238              */
1239             const QMetaMethod &current = _meta->method(_methodIds[method]);
1240             const QMetaMethod &candidate = _meta->method(i);
1241             if (current.parameterTypes().count() > candidate.parameterTypes().count()) {
1242                 int minCount = candidate.parameterTypes().count();
1243                 QList<QByteArray> commonParams = current.parameterTypes().mid(0, minCount);
1244                 if (commonParams == candidate.parameterTypes())
1245                     continue;  // we already got the full featured version
1246             }
1247             else {
1248                 int minCount = current.parameterTypes().count();
1249                 QList<QByteArray> commonParams = candidate.parameterTypes().mid(0, minCount);
1250                 if (commonParams == current.parameterTypes()) {
1251                     _methodIds[method] = i; // use the new one
1252                     continue;
1253                 }
1254             }
1255             if (checkConflicts) {
1256                 qWarning() << "class" << meta->className() << "contains overloaded methods which is currently not supported!";
1257                 qWarning() << " - " << _meta->method(i).signature() << "conflicts with" << _meta->method(_methodIds[method]).signature();
1258             }
1259             continue;
1260         }
1261         _methodIds[method] = i;
1262     }
1263 }
1264
1265
1266 const SignalProxy::ExtendedMetaObject::MethodDescriptor &SignalProxy::ExtendedMetaObject::methodDescriptor(int methodId)
1267 {
1268     if (!_methods.contains(methodId)) {
1269         _methods[methodId] = MethodDescriptor(_meta->method(methodId));
1270     }
1271     return _methods[methodId];
1272 }
1273
1274
1275 const QHash<int, int> &SignalProxy::ExtendedMetaObject::receiveMap()
1276 {
1277     if (_receiveMap.isEmpty()) {
1278         QHash<int, int> receiveMap;
1279
1280         QMetaMethod requestSlot;
1281         QByteArray returnTypeName;
1282         QByteArray signature;
1283         QByteArray methodName;
1284         QByteArray params;
1285         int paramsPos;
1286         int receiverId;
1287         const int methodCount = _meta->methodCount();
1288         for (int i = 0; i < methodCount; i++) {
1289             requestSlot = _meta->method(i);
1290             if (requestSlot.methodType() != QMetaMethod::Slot)
1291                 continue;
1292
1293             returnTypeName = requestSlot.typeName();
1294             if (QMetaType::Void == (QMetaType::Type)returnType(i))
1295                 continue;
1296
1297             signature = QByteArray(requestSlot.signature());
1298             if (!signature.startsWith("request"))
1299                 continue;
1300
1301             paramsPos = signature.indexOf('(');
1302             if (paramsPos == -1)
1303                 continue;
1304
1305             methodName = signature.left(paramsPos);
1306             params = signature.mid(paramsPos);
1307
1308             methodName = methodName.replace("request", "receive");
1309             params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
1310
1311             signature = QMetaObject::normalizedSignature(methodName + params);
1312             receiverId = _meta->indexOfSlot(signature);
1313
1314             if (receiverId == -1) {
1315                 signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
1316                 receiverId = _meta->indexOfSlot(signature);
1317             }
1318
1319             if (receiverId != -1) {
1320                 receiveMap[i] = receiverId;
1321             }
1322         }
1323         _receiveMap = receiveMap;
1324     }
1325     return _receiveMap;
1326 }
1327
1328
1329 QByteArray SignalProxy::ExtendedMetaObject::methodName(const QMetaMethod &method)
1330 {
1331     QByteArray sig(method.signature());
1332     return sig.left(sig.indexOf("("));
1333 }
1334
1335
1336 QString SignalProxy::ExtendedMetaObject::methodBaseName(const QMetaMethod &method)
1337 {
1338     QString methodname = QString(method.signature()).section("(", 0, 0);
1339
1340     // determine where we have to chop:
1341     int upperCharPos;
1342     if (method.methodType() == QMetaMethod::Slot) {
1343         // we take evertyhing from the first uppercase char if it's slot
1344         upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
1345         if (upperCharPos == -1)
1346             return QString();
1347         methodname = methodname.mid(upperCharPos);
1348     }
1349     else {
1350         // and if it's a signal we discard everything from the last uppercase char
1351         upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
1352         if (upperCharPos == -1)
1353             return QString();
1354         methodname = methodname.left(upperCharPos);
1355     }
1356
1357     methodname[0] = methodname[0].toUpper();
1358
1359     return methodname;
1360 }
1361
1362
1363 SignalProxy::ExtendedMetaObject::MethodDescriptor::MethodDescriptor(const QMetaMethod &method)
1364     : _methodName(SignalProxy::ExtendedMetaObject::methodName(method)),
1365     _returnType(QMetaType::type(method.typeName()))
1366 {
1367     // determine argTypes
1368     QList<QByteArray> paramTypes = method.parameterTypes();
1369     QList<int> argTypes;
1370     for (int i = 0; i < paramTypes.count(); i++) {
1371         argTypes.append(QMetaType::type(paramTypes[i]));
1372     }
1373     _argTypes = argTypes;
1374
1375     // determine minArgCount
1376     QString signature(method.signature());
1377     _minArgCount = method.parameterTypes().count() - signature.count("=");
1378
1379     _receiverMode = (_methodName.startsWith("request"))
1380                     ? SignalProxy::Server
1381                     : SignalProxy::Client;
1382 }