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