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