1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Ford Motor Company
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "private/qmetaobjectbuilder_p.h"
41 
42 #include "qremoteobjectnode.h"
43 #include "qremoteobjectnode_p.h"
44 
45 #include "qremoteobjectregistry.h"
46 #include "qremoteobjectdynamicreplica.h"
47 #include "qremoteobjectpacket_p.h"
48 #include "qremoteobjectregistrysource_p.h"
49 #include "qremoteobjectreplica_p.h"
50 #include "qremoteobjectsource_p.h"
51 #include "qremoteobjectabstractitemmodelreplica_p.h"
52 #include "qremoteobjectabstractitemmodeladapter_p.h"
53 #include <QtCore/qabstractitemmodel.h>
54 #include <memory>
55 
56 QT_BEGIN_NAMESPACE
57 
58 using namespace QtRemoteObjects;
59 using namespace QRemoteObjectStringLiterals;
60 
61 using GadgetType = QVector<QVariant>;
62 using RegisteredType = QPair<GadgetType, std::shared_ptr<QMetaObject>>;
63 static QMutex s_managedTypesMutex;
64 static QHash<int, RegisteredType> s_managedTypes;
65 static QHash<int, QSet<IoDeviceBase*>> s_trackedConnections;
66 
GadgetsStaticMetacallFunction(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)67 static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
68 {
69     if (_c == QMetaObject::ReadProperty) {
70         GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
71         if (_id < _t->size()) {
72             const auto &prop = _t->at(_id);
73             QMetaType::destruct(int(prop.userType()), _a[0]);
74             QMetaType::construct(int(prop.userType()), _a[0], prop.constData());
75         }
76     } else if (_c == QMetaObject::WriteProperty) {
77         GadgetType *_t = reinterpret_cast<GadgetType *>(_o);
78         if (_id < _t->size()) {
79             auto & prop = (*_t)[_id];
80             prop = QVariant(prop.userType(), _a[0]);
81         }
82     }
83 }
84 
GadgetTypedDestructor(int,void * ptr)85 static void GadgetTypedDestructor(int, void *ptr)
86 {
87     reinterpret_cast<GadgetType*>(ptr)->~GadgetType();
88 }
89 
GadgetTypedConstructor(int type,void * where,const void * copy)90 static void *GadgetTypedConstructor(int type, void *where, const void *copy)
91 {
92     GadgetType *ret = where ? new(where) GadgetType : new GadgetType;
93     if (copy) {
94         *ret = *reinterpret_cast<const GadgetType*>(copy);
95     } else {
96         QMutexLocker lock(&s_managedTypesMutex);
97         auto it = s_managedTypes.find(type);
98         if (it == s_managedTypes.end()) {
99             delete ret;
100             return nullptr;
101         }
102         *ret = it->first;
103     }
104     return ret;
105 }
106 
GadgetSaveOperator(QDataStream & out,const void * data)107 static void GadgetSaveOperator(QDataStream & out, const void *data)
108 {
109     const GadgetType *gadgetProperties = reinterpret_cast<const GadgetType *>(data);
110     for (const auto &prop : *gadgetProperties)
111         out << prop;
112 }
113 
GadgetLoadOperator(QDataStream & in,void * data)114 static void GadgetLoadOperator(QDataStream &in, void *data)
115 {
116     GadgetType *gadgetProperties = reinterpret_cast<GadgetType *>(data);
117     for (auto &prop : *gadgetProperties)
118         in >> prop;
119 }
120 
121 // Like the Q_GADGET static methods above, we need constructor/destructor methods
122 // in order to use dynamically defined enums with QVariant or as signal/slot
123 // parameters (i.e., the queued connection mechanism, which QtRO leverages).
124 //
125 // We will need the enum methods to support different sizes when typed scope enum
126 // support is added, so might as well use that now.
127 template<typename T>
EnumDestructor(void * ptr)128 static void EnumDestructor(void *ptr)
129 {
130     static_cast<T*>(ptr)->~T();
131 }
132 
133 template<typename T>
EnumConstructor(void * where,const void * copy)134 static void *EnumConstructor(void *where, const void *copy)
135 {
136     T *ret = where ? new(where) T : new T;
137     if (copy)
138         *ret = *static_cast<const T*>(copy);
139     return ret;
140 }
141 
142 // Not used, but keeping these in case we end up with a need for save/load.
143 template<typename T>
EnumSaveOperator(QDataStream & out,const void * data)144 static void EnumSaveOperator(QDataStream & out, const void *data)
145 {
146     const T value = *static_cast<const T *>(data);
147     out << value;
148 }
149 
150 template<typename T>
EnumLoadOperator(QDataStream & in,void * data)151 static void EnumLoadOperator(QDataStream &in, void *data)
152 {
153     T value = *static_cast<T *>(data);
154     in >> value;
155 }
156 
name(const QMetaObject * const mobj)157 static QString name(const QMetaObject * const mobj)
158 {
159     const int ind = mobj->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
160     return ind >= 0 ? QString::fromLatin1(mobj->classInfo(ind).value()) : QString();
161 }
162 
getTypeNameAndMetaobjectFromClassInfo(const QMetaObject * & meta)163 QString QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(const QMetaObject *& meta) {
164     QString typeName;
165     const int ind = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
166     if (ind != -1) { //We have an object created from repc or at least with QCLASSINFO defined
167         typeName = QString::fromLatin1(meta->classInfo(ind).value());
168         while (true) {
169             Q_ASSERT(meta->superClass());//This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
170             //At the point superclass doesn't have the same QCLASSINFO_REMOTEOBJECT_TYPE,
171             //we have the metaobject we should work from
172             if (ind != meta->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE))
173                 break;
174             meta = meta->superClass();
175         }
176     }
177     return typeName;
178 }
179 
180 template <typename K, typename V, typename Query>
map_contains(const QMap<K,V> & map,const Query & key,typename QMap<K,V>::const_iterator & result)181 bool map_contains(const QMap<K,V> &map, const Query &key, typename QMap<K,V>::const_iterator &result)
182 {
183     const typename QMap<K,V>::const_iterator it = map.find(key);
184     if (it == map.end())
185         return false;
186     result = it;
187     return true;
188 }
189 
190 /*!
191     \qmltype Node
192     \instantiates QRemoteObjectNode
193     \inqmlmodule QtRemoteObjects
194     \brief A node on a Qt Remote Objects network.
195 
196     The Node type provides an entry point to a Qt Remote Objects network. A network
197     can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
198 
199     A Node does not have a url that other nodes can connect to, and thus is able to acquire
200     replicas only. It is not able to share source objects.
201 
202 */
203 
QRemoteObjectNodePrivate()204 QRemoteObjectNodePrivate::QRemoteObjectNodePrivate()
205     : QObjectPrivate()
206     , registry(nullptr)
207     , retryInterval(250)
208     , lastError(QRemoteObjectNode::NoError)
209     , persistedStore(nullptr)
210 { }
211 
~QRemoteObjectNodePrivate()212 QRemoteObjectNodePrivate::~QRemoteObjectNodePrivate()
213 {
214 }
215 
remoteObjectAddresses() const216 QRemoteObjectSourceLocations QRemoteObjectNodePrivate::remoteObjectAddresses() const
217 {
218     if (registry)
219         return registry->sourceLocations();
220     return QRemoteObjectSourceLocations();
221 }
222 
remoteObjectAddresses() const223 QRemoteObjectSourceLocations QRemoteObjectRegistryHostPrivate::remoteObjectAddresses() const
224 {
225     if (registrySource)
226         return registrySource->sourceLocations();
227     return QRemoteObjectSourceLocations();
228 }
229 
230 /*!
231     \reimp
232 */
timerEvent(QTimerEvent *)233 void QRemoteObjectNode::timerEvent(QTimerEvent*)
234 {
235     Q_D(QRemoteObjectNode);
236 
237     for (auto it = d->pendingReconnect.begin(), end = d->pendingReconnect.end(); it != end; /*erasing*/) {
238         const auto &conn = *it;
239         if (conn->isOpen()) {
240             it = d->pendingReconnect.erase(it);
241         } else {
242             conn->connectToServer();
243             ++it;
244         }
245     }
246 
247     if (d->pendingReconnect.isEmpty())
248         d->reconnectTimer.stop();
249 
250     qRODebug(this) << "timerEvent" << d->pendingReconnect.size();
251 }
252 
253 /*!
254     \qmlproperty int Node::heartbeatInterval
255 
256     Heartbeat interval in ms.
257 
258     The heartbeat (only helpful for socket connections) will periodically send a
259     message to connected nodes to detect whether the connection was disrupted.
260     Qt Remote Objects will try to reconnect automatically if it detects a dropped
261     connection. This function can help with that detection since the client will
262     only detect that the server is unavailable when it tries to send data.
263 
264     A value of \c 0 (the default) will disable the heartbeat.
265 */
266 
267 
268 /*!
269     \property QRemoteObjectNode::heartbeatInterval
270     \brief Heartbeat interval in ms.
271 
272     The heartbeat (only helpful for socket connections) will periodically send a
273     message to connected nodes to detect whether the connection was disrupted.
274     Qt Remote Objects will try to reconnect automatically if it detects a dropped
275     connection. This function can help with that detection since the client will
276     only detect that the server is unavailable when it tries to send data.
277 
278     A value of \c 0 (the default) will disable the heartbeat.
279 */
heartbeatInterval() const280 int QRemoteObjectNode::heartbeatInterval() const
281 {
282     Q_D(const QRemoteObjectNode);
283     return d->m_heartbeatInterval;
284 }
285 
setHeartbeatInterval(int interval)286 void QRemoteObjectNode::setHeartbeatInterval(int interval)
287 {
288     Q_D(QRemoteObjectNode);
289     if (d->m_heartbeatInterval == interval)
290         return;
291     d->m_heartbeatInterval = interval;
292     emit heartbeatIntervalChanged(interval);
293 }
294 
295 /*!
296     \since 5.12
297     \typedef QRemoteObjectNode::RemoteObjectSchemaHandler
298 
299     Typedef for a std::function method that can take a QUrl input and is
300     responsible for creating the communications channel between this node and
301     the node hosting the desired \l Source. As some types of QIODevices (e.g.,
302     QSslSocket) require additional steps before the device is ready for use,
303     the method is responsible for calling \l addClientSideConnection once the
304     connection is fully established.
305 */
306 
307 /*!
308     \since 5.12
309     \brief Provide a custom method to handle externally provided schemas
310 
311     This method is tied to the \l Registry and \l {External Schemas}. By
312     registering a std::function handler for an external schema, the registered
313     method will be called when the registry is notified of a \l Source you've
314     acquired being available. Without this registration, QtRO would only be
315     able to handle the "built-in" schemas.
316 
317     The provided method, \a handler, will be called when the registry sees a \l
318     Source object on a new (not yet connected) Node with a {QUrl::schema()} of
319     \a schema. The \a handler, of type \l
320     QRemoteObjectNode::RemoteObjectSchemaHandler will get the \l QUrl of the
321     Node providing the \l Source as an input parameter, and is responsible for
322     establishing the communications channel (a \l QIODevice of some sort) and
323     calling \l addClientSideConnection with it.
324 
325     \sa RemoteObjectSchemaHandler
326 */
registerExternalSchema(const QString & schema,QRemoteObjectNode::RemoteObjectSchemaHandler handler)327 void QRemoteObjectNode::registerExternalSchema(const QString &schema, QRemoteObjectNode::RemoteObjectSchemaHandler handler)
328 {
329     Q_D(QRemoteObjectNode);
330     d->schemaHandlers.insert(schema, handler);
331 }
332 
333 /*!
334     \since 5.11
335     \brief Forward Remote Objects from another network
336 
337     The proxy functionality is useful when you want to share \l Source objects
338     over multiple networks. For instance, if you have an embedded target using
339     target-only connections (like local) and you want to make some of those
340     same objects available externally.
341 
342     As a concrete example, say you have a set of processes talking to each
343     other on your target hardware using a registry, with the \l Registry at
344     "local:registry" and separate processes using a node at "local:MyHost" that
345     holds \l Source objects. If you wanted to access these objects, but over
346     tcp, you could create a new proxyNode like so:
347 
348 \code
349     // myInternalHost is a node only visible on the device...
350     QRemoteObjectHost myInternalHost("local:MyHost");
351     myInternalHost.enableRemoting<SomeObject>(&someObject);
352 
353     // Regular host node, listening on port 12123, so visible to other
354     // devices
355     QRemoteObjectHost proxyNode("tcp://localhost:12123");
356 
357     // Enable proxying objects from nodes on the local machine's internal
358     // QtRO bus
359     proxyNode.proxy("local:registry");
360 \endcode
361 
362     And from another device you create another node:
363 
364 \code
365     // NB: localhost resolves to a different ip address than proxyNode
366     QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
367 
368     // Connect to the target's proxyNode directly, or use a tcp registry...
369     nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
370 
371     // Because of the proxy, we can get the object over tcp/ip port 12123,
372     // even though we can't connect directly to "local:MyHost"
373     SomeObject *so = nodeOnRemoteDevice.acquire<SomeObject>();
374 \endcode
375 
376     This would (internally) create a node in proxyNode, which (again
377     internally/automatically) connects to the provided registry (given by the
378     \a registryUrl parameter, "local:registry" in this example). Whenever
379     local:registry emits the \l remoteObjectAdded signal, the
380     \c QRemoteObjectSourceLocation is passed to the \a filter given to the proxy
381     call. If this method returns true (the default filter simply returns true
382     without any filtering), the object is acquired() from the internal node and
383     enableRemoting() (once the replica is initialized) is called on proxyNode.
384 
385     If a \a hostUrl is provided (which is required to enable reverseProxy, but
386     not needed otherwise), the internal node will be a \l QRemoteObjectHost node
387     configured with the provided address. If no \a hostUrl is provided, the
388     internal node will be a QRemoteObjectNode (not HostNode).
389 
390     Returns \c true if the object is acquired from the internal node.
391 
392     \sa reverseProxy()
393 */
proxy(const QUrl & registryUrl,const QUrl & hostUrl,RemoteObjectNameFilter filter)394 bool QRemoteObjectHostBase::proxy(const QUrl &registryUrl, const QUrl &hostUrl, RemoteObjectNameFilter filter)
395 {
396     Q_D(QRemoteObjectHostBase);
397     if (!registryUrl.isValid() || !QtROClientFactory::instance()->isValid(registryUrl)) {
398         qROWarning(this) << "Can't proxy to registryUrl (invalid url or schema)" << registryUrl;
399         return false;
400     }
401 
402     if (!hostUrl.isEmpty() && !QtROClientFactory::instance()->isValid(hostUrl)) {
403         qROWarning(this) << "Can't proxy using hostUrl (invalid schema)" << hostUrl;
404         return false;
405     }
406 
407     if (d->proxyInfo) {
408         qROWarning(this) << "Proxying from multiple objects is currently not supported.";
409         return false;
410     }
411 
412     QRemoteObjectNode *node;
413     if (hostUrl.isEmpty()) {
414         node = new QRemoteObjectNode(registryUrl);
415     } else {
416         node = new QRemoteObjectHost(hostUrl, registryUrl);
417     }
418     d->proxyInfo = new ProxyInfo(node, this, filter);
419     return true;
420 }
421 
422 /*!
423     \since 5.11
424     \brief Forwards remote objects to another network.
425 
426     The reverseProxy() function allows the \l proxy() functionality to be
427     extended, in effect mirroring the proxy functionality in the "reverse"
428     direction. These are distinct, because node communication is not symmetric,
429     one side calls enableRemoting() with a \l Source object, the other side
430     calls acquire() to get a \l Replica. Using \l proxy() allows you to
431     "observe" objects on a target device remotely via acquire, but it does not
432     allow off-target \l Source objects to be acquired from the device's local:*
433     network. That is where \l reverseProxy() comes in. If a proxyNode is
434     created like so:
435 
436 \code
437     // myInternalHost is a node only visible on the device...
438     QRemoteObjectHost myInternalHost("local:MyHost");
439 
440     // RegistryHost node, listening on port 12123, so visible to other
441     // devices.  The node must be a RegistryHost, so the Sources on
442     // the "outside" network can be forwarded to the inner network.
443     QRemoteObjectRegistryHost proxyNode("tcp://localhost:12123");
444 
445     // Enable proxying objects from nodes on the local machine's internal
446     // QtRO bus.  Note the hostUrl parameter is now needed.
447     proxyNode.proxy("local:registry", "local:fromProxy");
448     proxyNode.reverseProxy();
449 \endcode
450 
451     And from another device you create another node:
452 
453 \code
454     // NB: localhost resolves to a different ip address than proxyNode
455     QRemoteObjectHost nodeOnRemoteDevice("tcp://localhost:23234");
456 
457     // Connect to the target's proxyNode directly, or use a tcp registry...
458     nodeOnRemoteDevice.connectToNode("tcp://<target device>:12123");
459 
460     // Because of the reverseProxy, we can expose objects on this device
461     // and they will make their way to proxyNode...
462     nodeOnRemoteDevice.enableRemoting<OtherObject>(&otherObject);
463 \endcode
464 
465 \code
466     // Acquire() can now see the objects on other devices through proxyNode,
467     // due to the reverseProxy call.
468     OtherObject *oo = myInternalHost.acquire<OtherObject>();
469 \endcode
470 
471     While the \l proxy() functionality allows \l Source objects on another
472     network to be acquired(), reverseProxy() allows \l Source objects to be
473     "pushed" to an otherwise inaccessible network.
474 
475     \note proxy() needs to be called before \l reverseProxy(), and a
476     hostUrl needs to be provided to \l proxy for \l reverseProxy() to work. The
477     \l reverseProxy() method allows a separate \a filter to be applied. This
478     reverseProxy specific filter will receive notifications of new \l Source
479     objects on proxyNode and acquire them on the internal node if they pass the
480     reverseFilter.
481 
482     Returns \c true on success, \c false otherwise.
483 
484     \sa proxy()
485 */
reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)486 bool QRemoteObjectHostBase::reverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
487 {
488     Q_D(QRemoteObjectHostBase);
489 
490     if (!d->proxyInfo) {
491         qROWarning(this) << "proxy() needs to be called before setting up reverse proxy.";
492         return false;
493     }
494 
495     QRemoteObjectHost *host = qobject_cast<QRemoteObjectHost *>(d->proxyInfo->proxyNode);
496     if (!host) {
497         qROWarning(this) << "proxy() needs called with host-url to enable reverse proxy.";
498         return false;
499     }
500 
501     return d->proxyInfo->setReverseProxy(filter);
502 }
503 
504 /*!
505     \internal The replica needs to have a default constructor to be able
506     to create a replica from QML.  In order for it to be properly
507     constructed, there needs to be a way to associate the replica with a
508     node and start the replica initialization.  Thus we need a public
509     method on node to facilitate that.  That's initializeReplica.
510 */
initializeReplica(QRemoteObjectReplica * instance,const QString & name)511 void QRemoteObjectNode::initializeReplica(QRemoteObjectReplica *instance, const QString &name)
512 {
513     Q_D(QRemoteObjectNode);
514     if (instance->inherits("QRemoteObjectDynamicReplica")) {
515         d->setReplicaImplementation(nullptr, instance, name);
516     } else {
517         const QMetaObject *meta = instance->metaObject();
518         // This is a templated acquire, so we tell the Source we don't need
519         // them to send the class definition.  Thus we need to store the
520         // metaObject for this class - if this is a nested class, the QObject
521         // could be a nullptr or updated from the source,
522         d->dynamicTypeManager.addFromMetaObject(meta);
523         d->setReplicaImplementation(meta, instance, name.isEmpty() ? ::name(meta) : name);
524     }
525 }
526 
setLastError(QRemoteObjectNode::ErrorCode errorCode)527 void QRemoteObjectNodePrivate::setLastError(QRemoteObjectNode::ErrorCode errorCode)
528 {
529     Q_Q(QRemoteObjectNode);
530     lastError = errorCode;
531     emit q->error(lastError);
532 }
533 
setReplicaImplementation(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)534 void QRemoteObjectNodePrivate::setReplicaImplementation(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
535 {
536     qROPrivDebug() << "Starting setReplicaImplementation for" << name;
537     openConnectionIfNeeded(name);
538     QMutexLocker locker(&mutex);
539     if (hasInstance(name)) {
540         qCDebug(QT_REMOTEOBJECT)<<"setReplicaImplementation - using existing instance";
541         QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
542         Q_ASSERT(rep);
543         instance->d_impl = rep;
544         rep->configurePrivate(instance);
545     } else {
546         instance->d_impl.reset(handleNewAcquire(meta, instance, name));
547         instance->initialize();
548         replicas.insert(name, instance->d_impl.toWeakRef());
549         qROPrivDebug() << "setReplicaImplementation - Created new instance" << name<<remoteObjectAddresses();
550     }
551 }
552 
553 /*!
554     Returns a pointer to the Node's \l {QRemoteObjectRegistry}, if the Node
555     is using the Registry feature; otherwise it returns 0.
556 */
registry() const557 const QRemoteObjectRegistry *QRemoteObjectNode::registry() const
558 {
559     Q_D(const QRemoteObjectNode);
560     return d->registry;
561 }
562 
563 /*!
564     \class QRemoteObjectAbstractPersistedStore
565     \inmodule QtRemoteObjects
566     \brief A class which provides the methods for setting PROP values of a
567     replica to value they had the last time the replica was used.
568 
569     This can be used to provide a "reasonable" value to be displayed until the
570     connection to the source is established and current values are available.
571 
572     This class must be overridden to provide an implementation for saving (\l
573     QRemoteObjectAbstractPersistedStore::saveProperties) and restoring (\l
574     QRemoteObjectAbstractPersistedStore::restoreProperties) PROP values. The derived
575     type can then be set for a node, and any replica acquired from that node
576     will then automatically store PERSISTED properties when the replica
577     destructor is called, and retrieve the values when the replica is
578     instantiated.
579 */
580 
581 /*!
582     Constructs a QRemoteObjectAbstractPersistedStore with the given \a parent.
583     The default value of \a parent is \c nullptr.
584 */
QRemoteObjectAbstractPersistedStore(QObject * parent)585 QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QObject *parent)
586     : QObject(parent)
587 {
588 }
589 
~QRemoteObjectAbstractPersistedStore()590 QRemoteObjectAbstractPersistedStore::~QRemoteObjectAbstractPersistedStore()
591 {
592 }
593 
594 /*!
595     \fn virtual void QRemoteObjectAbstractPersistedStore::saveProperties(const QString &repName, const QByteArray &repSig, const QVariantList &values)
596 
597     This method will be provided the replica class's \a repName, \a repSig and the list of
598     \a values that PERSISTED properties have when the replica destructor was
599     called. It is the responsibility of the inheriting class to store the
600     information in a manner consistent for \l
601     QRemoteObjectAbstractPersistedStore::restoreProperties to retrieve.
602 
603     \sa QRemoteObjectAbstractPersistedStore::restoreProperties
604 */
605 
606 /*!
607     \fn virtual QVariantList QRemoteObjectAbstractPersistedStore::restoreProperties(const QString &repName, const QByteArray &repSig)
608 
609     This method will be provided the replica class's \a repName and \a repSig when the
610     replica is being initialized. It is the responsibility of the inheriting
611     class to get the last values persisted by \l
612     QRemoteObjectAbstractPersistedStore::saveProperties and return them. An empty
613     QVariantList should be returned if no values are available.
614 
615     \sa QRemoteObjectAbstractPersistedStore::saveProperties
616 */
617 
618 
persistedStore() const619 QRemoteObjectAbstractPersistedStore *QRemoteObjectNode::persistedStore() const
620 {
621     Q_D(const QRemoteObjectNode);
622     return d->persistedStore;
623 }
624 
625 /*!
626     \qmlproperty QRemoteObjectAbstractPersistedStore Node::persistedStore
627 
628     Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
629 
630     Allows replica \l PROP members with the PERSISTED trait to save their current value when the
631     replica is deleted and restore a stored value the next time the replica is started.
632 
633     Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
634     persistence is handled. A default QSettings-based implementation is provided by SettingsStore.
635 */
636 
637 /*!
638     \since 5.11
639     \property QRemoteObjectNode::persistedStore
640     \brief Allows setting a \l QRemoteObjectAbstractPersistedStore instance for the node.
641 
642     Allows replica \l PROP members with the PERSISTED trait to save their current value when the
643     replica is deleted and restore a stored value the next time the replica is started.
644 
645     Requires a \l QRemoteObjectAbstractPersistedStore class implementation to control where and how
646     persistence is handled.
647 */
setPersistedStore(QRemoteObjectAbstractPersistedStore * persistedStore)648 void QRemoteObjectNode::setPersistedStore(QRemoteObjectAbstractPersistedStore *persistedStore)
649 {
650     Q_D(QRemoteObjectNode);
651     d->persistedStore = persistedStore;
652 }
653 
QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate & dptr,QObject * parent)654 QRemoteObjectAbstractPersistedStore::QRemoteObjectAbstractPersistedStore(QRemoteObjectAbstractPersistedStorePrivate &dptr, QObject *parent)
655     : QObject(dptr, parent)
656 {
657 }
658 
QRemoteObjectAbstractPersistedStorePrivate()659 QRemoteObjectAbstractPersistedStorePrivate::QRemoteObjectAbstractPersistedStorePrivate()
660 {
661 }
662 
~QRemoteObjectAbstractPersistedStorePrivate()663 QRemoteObjectAbstractPersistedStorePrivate::~QRemoteObjectAbstractPersistedStorePrivate()
664 {
665 }
666 
~QRemoteObjectMetaObjectManager()667 QRemoteObjectMetaObjectManager::~QRemoteObjectMetaObjectManager()
668 {
669     for (QMetaObject *mo : dynamicTypes)
670         free(mo); //QMetaObjectBuilder uses malloc, not new
671 }
672 
metaObjectForType(const QString & type)673 const QMetaObject *QRemoteObjectMetaObjectManager::metaObjectForType(const QString &type)
674 {
675     qCDebug(QT_REMOTEOBJECT) << "metaObjectForType: looking for" << type << "static keys:" << staticTypes.keys() << "dynamic keys:" << dynamicTypes.keys();
676     Q_ASSERT(staticTypes.contains(type) || dynamicTypes.contains(type));
677     auto it = staticTypes.constFind(type);
678     if (it != staticTypes.constEnd())
679         return it.value();
680     return dynamicTypes.value(type);
681 }
682 
trackConnection(int typeId,IoDeviceBase * connection)683 static void trackConnection(int typeId, IoDeviceBase *connection)
684 {
685     QMutexLocker lock(&s_managedTypesMutex);
686     if (s_trackedConnections[typeId].contains(connection))
687         return;
688     s_trackedConnections[typeId].insert(connection);
689     auto unregisterIfNotUsed = [typeId, connection]{
690         QMutexLocker lock(&s_managedTypesMutex);
691         Q_ASSERT(s_trackedConnections.contains(typeId));
692         Q_ASSERT(s_trackedConnections[typeId].contains(connection));
693         s_trackedConnections[typeId].remove(connection);
694         if (s_trackedConnections[typeId].isEmpty()) {
695             s_trackedConnections.remove(typeId);
696             s_managedTypes.remove(typeId);
697             QMetaType::unregisterType(typeId);
698         }
699     };
700 
701     // Unregister the type only when the connection is destroyed
702     // Do not unregister types when the connections is discconected, because
703     // if it gets reconnected it will not register the types again
704     QObject::connect(connection, &IoDeviceBase::destroyed, unregisterIfNotUsed);
705 }
706 
707 struct EnumPair {
708     QByteArray name;
709     int value;
710 };
711 
712 struct EnumData {
713     QByteArray name;
714     bool isFlag, isScoped;
715     quint32 keyCount, size;
716     QVector<EnumPair> values;
717 };
718 
719 struct GadgetProperty {
720     QByteArray name;
721     QByteArray type;
722 };
723 
724 struct GadgetData {
725     QVector<GadgetProperty> properties;
726     QVector<EnumData> enums;
727 };
728 
729 using Gadgets = QHash<QByteArray, GadgetData>;
730 
registerEnum(const QByteArray & name,const QMetaObject * meta,int size=4)731 static void registerEnum(const QByteArray &name, const QMetaObject *meta, int size=4)
732 {
733     // When we add support for enum classes, we will need to set this to something like
734     // QByteArray(enumClass).append("::").append(enumMeta.name()) when enumMeta.isScoped() is true.
735     // That is a new feature, though.
736     if (QMetaType::isRegistered(QMetaType::type(name)))
737         return;
738     static const auto flags = QMetaType::IsEnumeration | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
739     int id;
740     switch (size) {
741     case 1: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint8>,
742                                                  &EnumConstructor<qint8>, size, flags, meta);
743         break;
744     case 2: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint16>,
745                                                  &EnumConstructor<qint16>, size, flags, meta);
746         break;
747     case 4: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
748                                                  &EnumConstructor<qint32>, size, flags, meta);
749         break;
750     // Qt currently only supports enum values of 4 or less bytes (QMetaEnum value(index) returns int)
751 //    case 8: id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint64>,
752 //                                                 &EnumConstructor<qint64>, size, flags, meta);
753 //        break;
754     default:
755         qWarning() << "Invalid enum detected" << name << "with size" << size << ".  Defaulting to register as int.";
756         size = 4;
757         id = QMetaType::registerType(name.constData(), nullptr, nullptr, &EnumDestructor<qint32>,
758                                                  &EnumConstructor<qint32>, size, flags, meta);
759     }
760 #ifdef QTRO_VERBOSE_PROTOCOL
761     qDebug() << "Registering new enum with id" << id << name << "size:" << size;
762 #endif
763     qCDebug(QT_REMOTEOBJECT) << "Registering new enum with id" << id << name << "size:" << size;
764 }
765 
registerGadgets(IoDeviceBase * connection,Gadgets & gadgets,QByteArray typeName)766 static int registerGadgets(IoDeviceBase *connection, Gadgets &gadgets, QByteArray typeName)
767 {
768    const auto &gadget = gadgets.take(typeName);
769    int typeId = QMetaType::type(typeName);
770    if (typeId != QMetaType::UnknownType) {
771        trackConnection(typeId, connection);
772        return typeId;
773    }
774 
775    QMetaObjectBuilder gadgetBuilder;
776    gadgetBuilder.setClassName(typeName);
777    gadgetBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject | QMetaObjectBuilder::PropertyAccessInStaticMetaCall);
778    GadgetType gadgetType;
779    for (const auto &prop : gadget.properties) {
780        int propertyType = QMetaType::type(prop.type);
781        if (!propertyType && gadgets.contains(prop.type))
782            propertyType = registerGadgets(connection, gadgets, prop.type);
783        gadgetType.push_back(QVariant(QVariant::Type(propertyType)));
784        auto dynamicProperty = gadgetBuilder.addProperty(prop.name, prop.type);
785        dynamicProperty.setWritable(true);
786        dynamicProperty.setReadable(true);
787    }
788    for (const auto &enumData: gadget.enums) {
789        auto enumBuilder = gadgetBuilder.addEnumerator(enumData.name);
790        enumBuilder.setIsFlag(enumData.isFlag);
791        enumBuilder.setIsScoped(enumData.isScoped);
792 
793        for (quint32 k = 0; k < enumData.keyCount; ++k) {
794            const auto pair = enumData.values.at(k);
795            enumBuilder.addKey(pair.name, pair.value);
796        }
797    }
798    auto meta = gadgetBuilder.toMetaObject();
799    const auto enumCount = meta->enumeratorCount();
800    for (int i = 0; i < enumCount; i++) {
801        const QByteArray registeredName = QByteArray(typeName).append("::").append(meta->enumerator(i).name());
802        registerEnum(registeredName, meta, gadget.enums.at(i).size);
803    }
804    QMetaType::TypeFlags flags = QMetaType::IsGadget;
805    int gadgetTypeId;
806    if (meta->propertyCount()) {
807        meta->d.static_metacall = &GadgetsStaticMetacallFunction;
808        meta->d.superdata = nullptr;
809        flags |= QMetaType::NeedsConstruction | QMetaType::NeedsDestruction;
810        gadgetTypeId = QMetaType::registerType(typeName.constData(),
811                                               &GadgetTypedDestructor,
812                                               &GadgetTypedConstructor,
813                                               sizeof(GadgetType),
814                                               flags, meta);
815        QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator);
816    } else {
817        gadgetTypeId = QMetaType::registerType(typeName.constData(),
818                                               nullptr,
819                                               nullptr,
820                                               sizeof(GadgetType),
821                                               flags, meta);
822    }
823    trackConnection(gadgetTypeId, connection);
824    QMutexLocker lock(&s_managedTypesMutex);
825    s_managedTypes[gadgetTypeId] = qMakePair(gadgetType, std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }});
826    return gadgetTypeId;
827 }
828 
registerAllGadgets(IoDeviceBase * connection,Gadgets & gadgets)829 static void registerAllGadgets(IoDeviceBase *connection, Gadgets &gadgets)
830 {
831     while (!gadgets.isEmpty())
832         registerGadgets(connection, gadgets, gadgets.constBegin().key());
833 }
834 
deserializeEnum(QDataStream & ds,EnumData & enumData)835 static void deserializeEnum(QDataStream &ds, EnumData &enumData)
836 {
837     ds >> enumData.name;
838     ds >> enumData.isFlag;
839     ds >> enumData.isScoped;
840     ds >> enumData.size;
841     ds >> enumData.keyCount;
842     for (quint32 i = 0; i < enumData.keyCount; i++) {
843         EnumPair pair;
844         ds >> pair.name;
845         ds >> pair.value;
846         enumData.values.push_back(pair);
847     }
848 }
849 
parseGadgets(IoDeviceBase * connection,QDataStream & in)850 static void parseGadgets(IoDeviceBase *connection, QDataStream &in)
851 {
852     quint32 qtEnums, numGadgets;
853     in >> qtEnums; // Qt enums - just need registration
854     for (quint32 i = 0; i < qtEnums; ++i) {
855         QByteArray enumName;
856         in >> enumName;
857         QMetaType t(QMetaType::type(enumName.constData()));
858         registerEnum(enumName, t.metaObject()); // All Qt enums have default type int
859     }
860     in >> numGadgets;
861     if (numGadgets == 0)
862         return;
863     Gadgets gadgets;
864     for (quint32 i = 0; i < numGadgets; ++i) {
865         QByteArray type;
866         in >> type;
867         quint32 numProperties, numEnums;
868         in >> numProperties;
869         auto &properties = gadgets[type].properties;
870         for (quint32 p = 0; p < numProperties; ++p) {
871             GadgetProperty prop;
872             in >> prop.name;
873             in >> prop.type;
874             properties.push_back(prop);
875         }
876         in >> numEnums;
877         auto &enums = gadgets[type].enums;
878         for (quint32 e = 0; e < numEnums; ++e) {
879             EnumData enumData;
880             deserializeEnum(in, enumData);
881             enums.push_back(enumData);
882         }
883     }
884     registerAllGadgets(connection, gadgets);
885 }
886 
addDynamicType(IoDeviceBase * connection,QDataStream & in)887 QMetaObject *QRemoteObjectMetaObjectManager::addDynamicType(IoDeviceBase *connection, QDataStream &in)
888 {
889     QMetaObjectBuilder builder;
890     builder.setSuperClass(&QRemoteObjectReplica::staticMetaObject);
891     builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
892 
893     QString typeString;
894     QByteArray type;
895     quint32 numEnums = 0;
896     quint32 numSignals = 0;
897     quint32 numMethods = 0;
898     quint32 numProperties = 0;
899 
900     in >> typeString;
901     type = typeString.toLatin1();
902     builder.addClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE, type);
903     builder.setClassName(type);
904 
905     in >> numEnums;
906     QVector<quint32> enumSizes(numEnums);
907     for (quint32 i = 0; i < numEnums; ++i) {
908         EnumData enumData;
909         deserializeEnum(in, enumData);
910         auto enumBuilder = builder.addEnumerator(enumData.name);
911         enumBuilder.setIsFlag(enumData.isFlag);
912         enumBuilder.setIsScoped(enumData.isScoped);
913         enumSizes[i] = enumData.size;
914 
915         for (quint32 k = 0; k < enumData.keyCount; ++k) {
916             const auto pair = enumData.values.at(k);
917             enumBuilder.addKey(pair.name, pair.value);
918         }
919     }
920     parseGadgets(connection, in);
921 
922     int curIndex = 0;
923 
924     in >> numSignals;
925     for (quint32 i = 0; i < numSignals; ++i) {
926         QByteArray signature;
927         QList<QByteArray> paramNames;
928         in >> signature;
929         in >> paramNames;
930         ++curIndex;
931         auto mmb = builder.addSignal(signature);
932         mmb.setParameterNames(paramNames);
933     }
934 
935     in >> numMethods;
936     for (quint32 i = 0; i < numMethods; ++i) {
937         QByteArray signature, returnType;
938         QList<QByteArray> paramNames;
939         in >> signature;
940         in >> returnType;
941         in >> paramNames;
942         ++curIndex;
943         const bool isVoid = returnType.isEmpty() || returnType == QByteArrayLiteral("void");
944         QMetaMethodBuilder mmb;
945         if (isVoid)
946             mmb = builder.addMethod(signature);
947         else
948             mmb = builder.addMethod(signature, QByteArrayLiteral("QRemoteObjectPendingCall"));
949         mmb.setParameterNames(paramNames);
950     }
951 
952     in >> numProperties;
953 
954     for (quint32 i = 0; i < numProperties; ++i) {
955         QByteArray name;
956         QByteArray typeName;
957         QByteArray signalName;
958         in >> name;
959         in >> typeName;
960         in >> signalName;
961         if (signalName.isEmpty())
962             builder.addProperty(name, typeName);
963         else
964             builder.addProperty(name, typeName, builder.indexOfSignal(signalName));
965     }
966 
967     auto meta = builder.toMetaObject();
968     // Our type likely has enumerations from the inherited base classes, such as the Replica State
969     // We only want to register the new enumerations, and since we just added them, we know they
970     // are the last indices.  Thus a backwards count seems most efficient.
971     const int totalEnumCount = meta->enumeratorCount();
972     int incrementingIndex = 0;
973     for (int i = numEnums; i > 0; i--) {
974         auto const enumMeta = meta->enumerator(totalEnumCount - i);
975         const QByteArray registeredName = QByteArray(type).append("::").append(enumMeta.name());
976         registerEnum(registeredName, meta, enumSizes.at(incrementingIndex++));
977     }
978     dynamicTypes.insert(typeString, meta);
979     return meta;
980 }
981 
addFromMetaObject(const QMetaObject * metaObject)982 void QRemoteObjectMetaObjectManager::addFromMetaObject(const QMetaObject *metaObject)
983 {
984     QString className = QLatin1String(metaObject->className());
985     if (!className.endsWith(QLatin1String("Replica")))
986         return;
987     if (className == QLatin1String("QRemoteObjectDynamicReplica") || staticTypes.contains(className))
988         return;
989     className.chop(7); //Remove 'Replica' from name
990     staticTypes.insert(className, metaObject);
991 }
992 
connectReplica(QObject * object,QRemoteObjectReplica * instance)993 void QRemoteObjectNodePrivate::connectReplica(QObject *object, QRemoteObjectReplica *instance)
994 {
995     int nConnections = 0;
996     const QMetaObject *us = instance->metaObject();
997     const QMetaObject *them = object->metaObject();
998 
999     static const int memberOffset = QRemoteObjectReplica::staticMetaObject.methodCount();
1000     for (int idx = memberOffset; idx < us->methodCount(); ++idx) {
1001         const QMetaMethod mm = us->method(idx);
1002 
1003         qROPrivDebug() << idx << mm.name();
1004         if (mm.methodType() != QMetaMethod::Signal)
1005             continue;
1006 
1007         // try to connect to a signal on the parent that has the same method signature
1008         QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
1009         qROPrivDebug() << sig;
1010         if (them->indexOfSignal(sig.constData()) == -1)
1011             continue;
1012 
1013         sig.prepend(QSIGNAL_CODE + '0');
1014         const char * const csig = sig.constData();
1015         const bool res = QObject::connect(object, csig, instance, csig);
1016         Q_UNUSED(res);
1017         ++nConnections;
1018 
1019         qROPrivDebug() << sig << res;
1020     }
1021 
1022     qROPrivDebug() << "# connections =" << nConnections;
1023 }
1024 
openConnectionIfNeeded(const QString & name)1025 void QRemoteObjectNodePrivate::openConnectionIfNeeded(const QString &name)
1026 {
1027     qROPrivDebug() << Q_FUNC_INFO << name << this;
1028     if (!remoteObjectAddresses().contains(name)) {
1029         qROPrivDebug() << name << "not available - available addresses:" << remoteObjectAddresses();
1030         return;
1031     }
1032 
1033     if (!initConnection(remoteObjectAddresses().value(name).hostUrl))
1034         qROPrivWarning() << "failed to open connection to" << name;
1035 }
1036 
initConnection(const QUrl & address)1037 bool QRemoteObjectNodePrivate::initConnection(const QUrl &address)
1038 {
1039     Q_Q(QRemoteObjectNode);
1040     if (requestedUrls.contains(address)) {
1041         qROPrivDebug() << "Connection already requested for " << address.toString();
1042         return true;
1043     }
1044 
1045     requestedUrls.insert(address);
1046 
1047     if (schemaHandlers.contains(address.scheme())) {
1048         schemaHandlers[address.scheme()](address);
1049         return true;
1050     }
1051 
1052     ClientIoDevice *connection = QtROClientFactory::instance()->create(address, q);
1053     if (!connection) {
1054         qROPrivWarning() << "Could not create ClientIoDevice for client. Invalid url/scheme provided?" << address;
1055         return false;
1056     }
1057     qROPrivDebug() << "Opening connection to" << address.toString();
1058     qROPrivDebug() << "Replica Connection isValid" << connection->isOpen();
1059     QObject::connect(connection, &ClientIoDevice::shouldReconnect, q, [this, connection]() {
1060         onShouldReconnect(connection);
1061     });
1062     QObject::connect(connection, &IoDeviceBase::readyRead, q, [this, connection]() {
1063         onClientRead(connection);
1064     });
1065     connection->connectToServer();
1066 
1067     return true;
1068 }
1069 
hasInstance(const QString & name)1070 bool QRemoteObjectNodePrivate::hasInstance(const QString &name)
1071 {
1072     if (!replicas.contains(name))
1073         return false;
1074 
1075     QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(name).toStrongRef();
1076     if (!rep) { //already deleted
1077         replicas.remove(name);
1078         return false;
1079     }
1080 
1081     return true;
1082 }
1083 
onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation & entry)1084 void QRemoteObjectNodePrivate::onRemoteObjectSourceAdded(const QRemoteObjectSourceLocation &entry)
1085 {
1086     qROPrivDebug() << "onRemoteObjectSourceAdded" << entry << replicas << replicas.contains(entry.first);
1087     if (!entry.first.isEmpty()) {
1088         QRemoteObjectSourceLocations locs = registry->sourceLocations();
1089         locs[entry.first] = entry.second;
1090         //TODO Is there a way to extend QRemoteObjectSourceLocations in place?
1091         registry->d_impl->setProperty(0, QVariant::fromValue(locs));
1092         qROPrivDebug() << "onRemoteObjectSourceAdded, now locations =" << locs;
1093     }
1094     if (replicas.contains(entry.first)) //We have a replica waiting on this remoteObject
1095     {
1096         QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(entry.first).toStrongRef();
1097         if (!rep) { //replica has been deleted, remove from list
1098             replicas.remove(entry.first);
1099             return;
1100         }
1101 
1102         initConnection(entry.second.hostUrl);
1103 
1104         qROPrivDebug() << "Called initConnection due to new RemoteObjectSource added via registry" << entry.first;
1105     }
1106 }
1107 
onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation & entry)1108 void QRemoteObjectNodePrivate::onRemoteObjectSourceRemoved(const QRemoteObjectSourceLocation &entry)
1109 {
1110     if (!entry.first.isEmpty()) {
1111         QRemoteObjectSourceLocations locs = registry->sourceLocations();
1112         locs.remove(entry.first);
1113         registry->d_impl->setProperty(0, QVariant::fromValue(locs));
1114     }
1115 }
1116 
onRegistryInitialized()1117 void QRemoteObjectNodePrivate::onRegistryInitialized()
1118 {
1119     qROPrivDebug() << "Registry Initialized" << remoteObjectAddresses();
1120 
1121     const auto remotes = remoteObjectAddresses();
1122     for (auto i = remotes.cbegin(), end = remotes.cend(); i != end; ++i) {
1123         if (replicas.contains(i.key())) //We have a replica waiting on this remoteObject
1124         {
1125             QSharedPointer<QReplicaImplementationInterface> rep = replicas.value(i.key()).toStrongRef();
1126             if (rep && !requestedUrls.contains(i.value().hostUrl))
1127                 initConnection(i.value().hostUrl);
1128             else if (!rep) //replica has been deleted, remove from list
1129                 replicas.remove(i.key());
1130 
1131             continue;
1132         }
1133     }
1134 }
1135 
onShouldReconnect(ClientIoDevice * ioDevice)1136 void QRemoteObjectNodePrivate::onShouldReconnect(ClientIoDevice *ioDevice)
1137 {
1138     Q_Q(QRemoteObjectNode);
1139 
1140     const auto remoteObjects = ioDevice->remoteObjects();
1141     for (const QString &remoteObject : remoteObjects) {
1142         connectedSources.remove(remoteObject);
1143         ioDevice->removeSource(remoteObject);
1144         if (replicas.contains(remoteObject)) { //We have a replica waiting on this remoteObject
1145             QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(remoteObject).toStrongRef());
1146             if (rep && !rep->connectionToSource.isNull()) {
1147                 rep->setDisconnected();
1148             } else if (!rep) {
1149                 replicas.remove(remoteObject);
1150             }
1151         }
1152     }
1153     if (requestedUrls.contains(ioDevice->url())) {
1154         // Only try to reconnect to URLs requested via connectToNode
1155         // If we connected via registry, wait for the registry to see the node/source again
1156         pendingReconnect.insert(ioDevice);
1157         if (!reconnectTimer.isActive()) {
1158             reconnectTimer.start(retryInterval, q);
1159             qROPrivDebug() << "Starting reconnect timer";
1160         }
1161     } else {
1162         qROPrivDebug() << "Url" << ioDevice->url().toDisplayString().toLatin1()
1163                        << "lost.  We will reconnect Replicas if they reappear on the Registry.";
1164     }
1165 }
1166 
1167 //This version of handleNewAcquire creates a QConnectedReplica. If this is a
1168 //host node, the QRemoteObjectHostBasePrivate overload is called instead.
handleNewAcquire(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)1169 QReplicaImplementationInterface *QRemoteObjectNodePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
1170 {
1171     Q_Q(QRemoteObjectNode);
1172     QConnectedReplicaImplementation *rp = new QConnectedReplicaImplementation(name, meta, q);
1173     rp->configurePrivate(instance);
1174     if (connectedSources.contains(name)) { //Either we have a peer connections, or existing connection via registry
1175         handleReplicaConnection(connectedSources[name].objectSignature, rp, connectedSources[name].device);
1176     } else {
1177         //No existing connection, but we know we can connect via registry
1178         const auto &sourceLocations = remoteObjectAddresses();
1179         const auto it = sourceLocations.constFind(name);
1180         // This will try the connection, and if successful, the remoteObjects will be sent
1181         // The link to the replica will be handled then
1182         if (it != sourceLocations.constEnd())
1183             initConnection(it.value().hostUrl);
1184     }
1185     return rp;
1186 }
1187 
handleReplicaConnection(const QString & name)1188 void QRemoteObjectNodePrivate::handleReplicaConnection(const QString &name)
1189 {
1190     QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(name).toStrongRef());
1191     if (!rep) { //replica has been deleted, remove from list
1192         replicas.remove(name);
1193         return;
1194     }
1195 
1196     if (rep->isShortCircuit())
1197         return;
1198 
1199     QConnectedReplicaImplementation *connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
1200     if (connectedRep->connectionToSource.isNull()) {
1201         const auto sourceInfo = connectedSources.value(name);
1202         handleReplicaConnection(sourceInfo.objectSignature, connectedRep, sourceInfo.device);
1203     }
1204 }
1205 
handleReplicaConnection(const QByteArray & sourceSignature,QConnectedReplicaImplementation * rep,IoDeviceBase * connection)1206 void QRemoteObjectNodePrivate::handleReplicaConnection(const QByteArray &sourceSignature, QConnectedReplicaImplementation *rep, IoDeviceBase *connection)
1207 {
1208     if (!checkSignatures(rep->m_objectSignature, sourceSignature)) {
1209         qROPrivWarning() << "Signature mismatch for" << rep->m_metaObject->className() << (rep->m_objectName.isEmpty() ? QLatin1String("(unnamed)") : rep->m_objectName);
1210         rep->setState(QRemoteObjectReplica::SignatureMismatch);
1211         return;
1212     }
1213     rep->setConnection(connection);
1214 }
1215 
1216 //Host Nodes can use the more efficient InProcess Replica if we (this Node) hold the Source for the
1217 //requested Replica.  If not, fall back to the Connected Replica case.
handleNewAcquire(const QMetaObject * meta,QRemoteObjectReplica * instance,const QString & name)1218 QReplicaImplementationInterface *QRemoteObjectHostBasePrivate::handleNewAcquire(const QMetaObject *meta, QRemoteObjectReplica *instance, const QString &name)
1219 {
1220     QMap<QString, QRemoteObjectSourceBase*>::const_iterator mapIt;
1221     if (remoteObjectIo && map_contains(remoteObjectIo->m_sourceObjects, name, mapIt)) {
1222         Q_Q(QRemoteObjectHostBase);
1223         QInProcessReplicaImplementation *rp = new QInProcessReplicaImplementation(name, meta, q);
1224         rp->configurePrivate(instance);
1225         connectReplica(mapIt.value()->m_object, instance);
1226         rp->connectionToSource = mapIt.value();
1227         return rp;
1228     }
1229     return QRemoteObjectNodePrivate::handleNewAcquire(meta, instance, name);
1230 }
1231 
onClientRead(QObject * obj)1232 void QRemoteObjectNodePrivate::onClientRead(QObject *obj)
1233 {
1234     using namespace QRemoteObjectPackets;
1235     IoDeviceBase *connection = qobject_cast<IoDeviceBase*>(obj);
1236     QRemoteObjectPacketTypeEnum packetType;
1237     Q_ASSERT(connection);
1238 
1239     do {
1240         if (!connection->read(packetType, rxName))
1241             return;
1242 
1243         if (packetType != Handshake && !m_handshakeReceived) {
1244             qROPrivWarning() << "Expected Handshake, got " << packetType;
1245             setLastError(QRemoteObjectNode::ProtocolMismatch);
1246             connection->close();
1247             break;
1248         }
1249 
1250         switch (packetType) {
1251         case Pong:
1252         {
1253             QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1254             if (rep)
1255                 rep->notifyAboutReply(0, {});
1256             else //replica has been deleted, remove from list
1257                 replicas.remove(rxName);
1258             break;
1259         }
1260         case Handshake:
1261             if (rxName != QtRemoteObjects::protocolVersion) {
1262                 qWarning() << "*** Protocol Mismatch, closing connection ***. Got" << rxName << "expected" << QtRemoteObjects::protocolVersion;
1263                 setLastError(QRemoteObjectNode::ProtocolMismatch);
1264                 connection->close();
1265             } else {
1266                 m_handshakeReceived = true;
1267             }
1268             break;
1269         case ObjectList:
1270         {
1271             deserializeObjectListPacket(connection->stream(), rxObjects);
1272             qROPrivDebug() << "newObjects:" << rxObjects;
1273             // We need to make sure all of the source objects are in connectedSources before we add connections,
1274             // otherwise nested QObjects could fail (we want to acquire children before parents, and the object
1275             // list is unordered)
1276             for (const auto &remoteObject : qAsConst(rxObjects)) {
1277                 qROPrivDebug() << "  connectedSources.contains(" << remoteObject << ")" << connectedSources.contains(remoteObject.name) << replicas.contains(remoteObject.name);
1278                 if (!connectedSources.contains(remoteObject.name)) {
1279                     connectedSources[remoteObject.name] = SourceInfo{connection, remoteObject.typeName, remoteObject.signature};
1280                     connection->addSource(remoteObject.name);
1281                     // Make sure we handle Registry first if it is available
1282                     if (remoteObject.name == QLatin1String("Registry") && replicas.contains(remoteObject.name))
1283                         handleReplicaConnection(remoteObject.name);
1284                 }
1285             }
1286             for (const auto &remoteObject : qAsConst(rxObjects)) {
1287                 if (replicas.contains(remoteObject.name)) //We have a replica waiting on this remoteObject
1288                     handleReplicaConnection(remoteObject.name);
1289             }
1290             break;
1291         }
1292         case InitPacket:
1293         {
1294             qROPrivDebug() << "InitPacket-->" << rxName << this;
1295             QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1296             //Use m_rxArgs (a QVariantList to hold the properties QVariantList)
1297             deserializeInitPacket(connection->stream(), rxArgs);
1298             if (rep)
1299             {
1300                 handlePointerToQObjectProperties(rep.data(), rxArgs);
1301                 rep->initialize(rxArgs);
1302             } else { //replica has been deleted, remove from list
1303                 replicas.remove(rxName);
1304             }
1305             break;
1306         }
1307         case InitDynamicPacket:
1308         {
1309             qROPrivDebug() << "InitDynamicPacket-->" << rxName << this;
1310             const QMetaObject *meta = dynamicTypeManager.addDynamicType(connection, connection->stream());
1311             deserializeInitPacket(connection->stream(), rxArgs);
1312             QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1313             if (rep)
1314             {
1315                 rep->setDynamicMetaObject(meta);
1316                 handlePointerToQObjectProperties(rep.data(), rxArgs);
1317                 rep->setDynamicProperties(rxArgs);
1318             } else { //replica has been deleted, remove from list
1319                 replicas.remove(rxName);
1320             }
1321             break;
1322         }
1323         case RemoveObject:
1324         {
1325             qROPrivDebug() << "RemoveObject-->" << rxName << this;
1326             connectedSources.remove(rxName);
1327             connection->removeSource(rxName);
1328             if (replicas.contains(rxName)) { //We have a replica using the removed source
1329                 QSharedPointer<QConnectedReplicaImplementation> rep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(rxName).toStrongRef());
1330                 if (rep && !rep->connectionToSource.isNull()) {
1331                     rep->connectionToSource.clear();
1332                     rep->setState(QRemoteObjectReplica::Suspect);
1333                 } else if (!rep) {
1334                     replicas.remove(rxName);
1335                 }
1336             }
1337             break;
1338         }
1339         case PropertyChangePacket:
1340         {
1341             int propertyIndex;
1342             deserializePropertyChangePacket(connection->stream(), propertyIndex, rxValue);
1343             QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1344             if (rep) {
1345                 QConnectedReplicaImplementation *connectedRep = nullptr;
1346                 if (!rep->isShortCircuit()) {
1347                     connectedRep = static_cast<QConnectedReplicaImplementation *>(rep.data());
1348                     if (!connectedRep->childIndices().contains(propertyIndex))
1349                         connectedRep = nullptr; //connectedRep will be a valid pointer only if propertyIndex is a child index
1350                 }
1351                 if (connectedRep)
1352                     rep->setProperty(propertyIndex, handlePointerToQObjectProperty(connectedRep, propertyIndex, rxValue));
1353                 else {
1354                     const QMetaProperty property = rep->m_metaObject->property(propertyIndex + rep->m_metaObject->propertyOffset());
1355                     if (property.userType() == QMetaType::QVariant && rxValue.canConvert<QRO_>()) {
1356                         // This is a type that requires registration
1357                         QRO_ typeInfo = rxValue.value<QRO_>();
1358                         QDataStream in(typeInfo.classDefinition);
1359                         parseGadgets(connection, in);
1360                         QDataStream ds(typeInfo.parameters);
1361                         ds >> rxValue;
1362                     }
1363                     rep->setProperty(propertyIndex, decodeVariant(rxValue, property.userType()));
1364                 }
1365             } else { //replica has been deleted, remove from list
1366                 replicas.remove(rxName);
1367             }
1368             break;
1369         }
1370         case InvokePacket:
1371         {
1372             int call, index, serialId, propertyIndex;
1373             deserializeInvokePacket(connection->stream(), call, index, rxArgs, serialId, propertyIndex);
1374             QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1375             if (rep) {
1376                 static QVariant null(QMetaType::QObjectStar, (void*)0);
1377                 QVariant paramValue;
1378                 // Qt usually supports 9 arguments, so ten should be usually safe
1379                 QVarLengthArray<void*, 10> param(rxArgs.size() + 1);
1380                 param[0] = null.data(); //Never a return value
1381                 if (rxArgs.size()) {
1382                     auto signal = rep->m_metaObject->method(index+rep->m_signalOffset);
1383                     for (int i = 0; i < rxArgs.size(); i++) {
1384                         if (signal.parameterType(i) == QMetaType::QVariant)
1385                             param[i + 1] = const_cast<void*>(reinterpret_cast<const void*>(&rxArgs.at(i)));
1386                         else {
1387                             decodeVariant(rxArgs[i], signal.parameterType(i));
1388                             param[i + 1] = const_cast<void *>(rxArgs.at(i).data());
1389                         }
1390                     }
1391                 } else if (propertyIndex != -1) {
1392                     param.resize(2);
1393                     paramValue = rep->getProperty(propertyIndex);
1394                     param[1] = paramValue.data();
1395                 }
1396                 qROPrivDebug() << "Replica Invoke-->" << rxName << rep->m_metaObject->method(index+rep->m_signalOffset).name() << index << rep->m_signalOffset;
1397                 // We activate on rep->metaobject() so the private metacall is used, not m_metaobject (which
1398                 // is the class thie replica looks like)
1399                 QMetaObject::activate(rep.data(), rep->metaObject(), index+rep->m_signalOffset, param.data());
1400             } else { //replica has been deleted, remove from list
1401                 replicas.remove(rxName);
1402             }
1403             break;
1404         }
1405         case InvokeReplyPacket:
1406         {
1407             int ackedSerialId;
1408             deserializeInvokeReplyPacket(connection->stream(), ackedSerialId, rxValue);
1409             QSharedPointer<QRemoteObjectReplicaImplementation> rep = qSharedPointerCast<QRemoteObjectReplicaImplementation>(replicas.value(rxName).toStrongRef());
1410             if (rep) {
1411                 qROPrivDebug() << "Received InvokeReplyPacket ack'ing serial id:" << ackedSerialId;
1412                 rep->notifyAboutReply(ackedSerialId, rxValue);
1413             } else { //replica has been deleted, remove from list
1414                 replicas.remove(rxName);
1415             }
1416             break;
1417         }
1418         case AddObject:
1419         case Invalid:
1420         case Ping:
1421             qROPrivWarning() << "Unexpected packet received";
1422         }
1423     } while (connection->bytesAvailable()); // have bytes left over, so do another iteration
1424 }
1425 
1426 /*!
1427     \class QRemoteObjectNode
1428     \inmodule QtRemoteObjects
1429     \brief A node on a Qt Remote Objects network.
1430 
1431     The QRemoteObjectNode class provides an entry point to a QtRemoteObjects
1432     network. A network can be as simple as two nodes, or an arbitrarily complex
1433     set of processes and devices.
1434 
1435     A QRemoteObjectNode does not have a url that other nodes can connect to,
1436     and thus is able to acquire replicas only. It is not able to share source
1437     objects (only QRemoteObjectHost and QRemoteObjectRegistryHost Nodes can
1438     share).
1439 
1440     Nodes may connect to each other directly using \l connectToNode, or
1441     they can use the QRemoteObjectRegistry to simplify connections.
1442 
1443     The QRemoteObjectRegistry is a special replica available to every node that
1444     connects to the Registry Url. It knows how to connect to every
1445     QRemoteObjectSource object on the network.
1446 
1447     \sa QRemoteObjectHost, QRemoteObjectRegistryHost
1448 */
1449 
1450 /*!
1451     \class QRemoteObjectHostBase
1452     \inmodule QtRemoteObjects
1453     \brief The QRemoteObjectHostBase class provides base functionality common to \l
1454     {QRemoteObjectHost} {Host} and \l {QRemoteObjectRegistryHost} {RegistryHost} classes.
1455 
1456     QRemoteObjectHostBase is a base class that cannot be instantiated directly.
1457     It provides the enableRemoting and disableRemoting functionality shared by
1458     all host nodes (\l {QRemoteObjectHost} {Host} and \l
1459     {QRemoteObjectRegistryHost} {RegistryHost}) as well as the logic required
1460     to expose \l {Source} objects on the Remote Objects network.
1461 */
1462 
1463 /*!
1464     \class QRemoteObjectHost
1465     \inmodule QtRemoteObjects
1466     \brief A (Host) Node on a Qt Remote Objects network.
1467 
1468     The QRemoteObjectHost class provides an entry point to a QtRemoteObjects
1469     network. A network can be as simple as two nodes, or an arbitrarily complex
1470     set of processes and devices.
1471 
1472     QRemoteObjectHosts have the same capabilities as QRemoteObjectNodes, but
1473     they can also be connected to and can share source objects on the network.
1474 
1475     Nodes may connect to each other directly using \l connectToNode, or they
1476     can use the QRemoteObjectRegistry to simplify connections.
1477 
1478     The QRemoteObjectRegistry is a special replica available to every node that
1479     connects to the uegistry Url. It knows how to connect to every
1480     QRemoteObjectSource object on the network.
1481 
1482     \sa QRemoteObjectNode, QRemoteObjectRegistryHost
1483 */
1484 
1485 /*!
1486     \class QRemoteObjectRegistryHost
1487     \inmodule QtRemoteObjects
1488     \brief A (Host/Registry) node on a Qt Remote Objects network.
1489 
1490     The QRemoteObjectRegistryHost class provides an entry point to a QtRemoteObjects
1491     network. A network can be as simple as two Nodes, or an arbitrarily complex
1492     set of processes and devices.
1493 
1494     A QRemoteObjectRegistryHost has the same capability that a
1495     QRemoteObjectHost has (which includes everything a QRemoteObjectNode
1496     supports), and in addition is the owner of the Registry. Any
1497     QRemoteObjectHost node that connects to this Node will have all of their
1498     Source objects made available by the Registry.
1499 
1500     Nodes only support connection to one \l registry, calling \l
1501     QRemoteObjectNode::setRegistryUrl when a Registry is already set is
1502     considered an error. For something like a secure and insecure network
1503     (where different Registries would be applicable), the recommendation is to
1504     create separate Nodes to connect to each, in effect creating two
1505     independent Qt Remote Objects networks.
1506 
1507     Nodes may connect to each other directly using \l connectToNode, or they
1508     can use the QRemoteObjectRegistry to simplify connections.
1509 
1510     The QRemoteObjectRegistry is a special Replica available to every Node that
1511     connects to the Registry Url. It knows how to connect to every
1512     QRemoteObjectSource object on the network.
1513 
1514     \sa QRemoteObjectNode, QRemoteObjectHost
1515 */
1516 
1517 /*!
1518     \enum QRemoteObjectNode::ErrorCode
1519 
1520     This enum type specifies the various error codes associated with QRemoteObjectNode errors:
1521 
1522     \value NoError No error.
1523     \value RegistryNotAcquired The registry could not be acquired.
1524     \value RegistryAlreadyHosted The registry is already defined and hosting Sources.
1525     \value NodeIsNoServer The given QRemoteObjectNode is not a host node.
1526     \value ServerAlreadyCreated The host node has already been initialized.
1527     \value UnintendedRegistryHosting An attempt was made to create a host QRemoteObjectNode and connect to itself as the registry.
1528     \value OperationNotValidOnClientNode The attempted operation is not valid on a client QRemoteObjectNode.
1529     \value SourceNotRegistered The given QRemoteObjectSource is not registered on this node.
1530     \value MissingObjectName The given QObject does not have objectName() set.
1531     \value HostUrlInvalid The given url has an invalid or unrecognized scheme.
1532     \value ProtocolMismatch The client and the server have different protocol versions.
1533     \value ListenFailed Can't listen on the specified host port.
1534 */
1535 
1536 /*!
1537     \enum QRemoteObjectHostBase::AllowedSchemas
1538 
1539     This enum is used to specify whether a Node will accept a url with an
1540     unrecognized schema for the hostUrl. By default only urls with known
1541     schemas are accepted, but using \c AllowExternalRegistration will enable
1542     the \l Registry to pass your external (to QtRO) url to client Nodes.
1543 
1544     \value BuiltInSchemasOnly Only allow the hostUrl to be set to a QtRO
1545     supported schema. This is the default value, and causes a Node error to be
1546     set if an unrecognized schema is provided.
1547     \value AllowExternalRegistration The provided schema is registered as an
1548     \l {External Schemas}{External Schema}
1549 
1550     \sa QRemoteObjectHost
1551 */
1552 
1553 /*!
1554     \fn ObjectType *QRemoteObjectNode::acquire(const QString &name)
1555 
1556     Returns a pointer to a Replica of type ObjectType (which is a template
1557     parameter and must inherit from \l QRemoteObjectReplica). That is, the
1558     template parameter must be a \l {repc} generated type. The \a name
1559     parameter can be used to specify the \a name given to the object
1560     during the QRemoteObjectHost::enableRemoting() call.
1561 */
1562 
initialize()1563 void QRemoteObjectNodePrivate::initialize()
1564 {
1565     qRegisterMetaType<QRemoteObjectNode *>();
1566     qRegisterMetaType<QRemoteObjectNode::ErrorCode>();
1567     qRegisterMetaType<QAbstractSocket::SocketError>(); //For queued qnx error()
1568     qRegisterMetaTypeStreamOperators<QVector<int> >();
1569     qRegisterMetaTypeStreamOperators<QRemoteObjectPackets::QRO_>();
1570     // To support dynamic MODELs, we need to make sure the types are registered
1571     QAbstractItemModelSourceAdapter::registerTypes();
1572 }
1573 
checkSignatures(const QByteArray & a,const QByteArray & b)1574 bool QRemoteObjectNodePrivate::checkSignatures(const QByteArray &a, const QByteArray &b)
1575 {
1576     // if any of a or b is empty it means it's a dynamic ojects or an item model
1577     if (a.isEmpty() || b.isEmpty())
1578         return true;
1579     return a == b;
1580 }
1581 
1582 
persistProperties(const QString & repName,const QByteArray & repSig,const QVariantList & props)1583 void QRemoteObjectNode::persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props)
1584 {
1585     Q_D(QRemoteObjectNode);
1586     if (d->persistedStore) {
1587         d->persistedStore->saveProperties(repName, repSig, props);
1588     } else {
1589         qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to store persisted properties for" << repName;
1590         qCWarning(QT_REMOTEOBJECT) << "    No persisted store set.";
1591     }
1592 }
1593 
retrieveProperties(const QString & repName,const QByteArray & repSig)1594 QVariantList QRemoteObjectNode::retrieveProperties(const QString &repName, const QByteArray &repSig)
1595 {
1596     Q_D(QRemoteObjectNode);
1597     if (d->persistedStore) {
1598         return d->persistedStore->restoreProperties(repName, repSig);
1599     }
1600     qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "Unable to retrieve persisted properties for" << repName;
1601     qCWarning(QT_REMOTEOBJECT) << "    No persisted store set.";
1602     return QVariantList();
1603 }
1604 
1605 /*!
1606     Default constructor for QRemoteObjectNode with the given \a parent. A Node
1607     constructed in this manner can not be connected to, and thus can not expose
1608     Source objects on the network. It also will not include a \l
1609     QRemoteObjectRegistry, unless set manually using setRegistryUrl.
1610 
1611     \sa connectToNode, setRegistryUrl
1612 */
QRemoteObjectNode(QObject * parent)1613 QRemoteObjectNode::QRemoteObjectNode(QObject *parent)
1614     : QObject(*new QRemoteObjectNodePrivate, parent)
1615 {
1616     Q_D(QRemoteObjectNode);
1617     d->initialize();
1618 }
1619 
1620 /*!
1621     QRemoteObjectNode connected to a {QRemoteObjectRegistry} {Registry}. A Node
1622     constructed in this manner can not be connected to, and thus can not expose
1623     Source objects on the network. Finding and connecting to other (Host) Nodes
1624     is handled by the QRemoteObjectRegistry specified by \a registryAddress.
1625 
1626     \sa connectToNode, setRegistryUrl, QRemoteObjectHost, QRemoteObjectRegistryHost
1627 */
QRemoteObjectNode(const QUrl & registryAddress,QObject * parent)1628 QRemoteObjectNode::QRemoteObjectNode(const QUrl &registryAddress, QObject *parent)
1629     : QObject(*new QRemoteObjectNodePrivate, parent)
1630 {
1631     Q_D(QRemoteObjectNode);
1632     d->initialize();
1633     setRegistryUrl(registryAddress);
1634 }
1635 
QRemoteObjectNode(QRemoteObjectNodePrivate & dptr,QObject * parent)1636 QRemoteObjectNode::QRemoteObjectNode(QRemoteObjectNodePrivate &dptr, QObject *parent)
1637     : QObject(dptr, parent)
1638 {
1639     Q_D(QRemoteObjectNode);
1640     d->initialize();
1641 }
1642 
1643 /*!
1644     \qmltype Host
1645     \instantiates QRemoteObjectHost
1646     \inqmlmodule QtRemoteObjects
1647     \brief A host node on a Qt Remote Objects network.
1648 
1649     The Host type provides an entry point to a Qt Remote Objects network. A network
1650     can be as simple as two nodes, or an arbitrarily complex set of processes and devices.
1651 
1652     Hosts have the same capabilities as Nodes, but they can also be connected to and can
1653     share source objects on the network.
1654 */
1655 
1656 /*!
1657     \internal This is a base class for both QRemoteObjectHost and
1658     QRemoteObjectRegistryHost to provide the shared features/functions for
1659     sharing \l Source objects.
1660 */
QRemoteObjectHostBase(QRemoteObjectHostBasePrivate & d,QObject * parent)1661 QRemoteObjectHostBase::QRemoteObjectHostBase(QRemoteObjectHostBasePrivate &d, QObject *parent)
1662     : QRemoteObjectNode(d, parent)
1663 { }
1664 
1665 /*!
1666     Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1667     exposing \l Source objects on the QtRO network) with the given \a parent.
1668     This constructor is meant specific to support QML in the future as it will
1669     not be available to connect to until \l {QRemoteObjectHost::}{setHostUrl} is called.
1670 
1671     \sa setHostUrl(), setRegistryUrl()
1672 */
QRemoteObjectHost(QObject * parent)1673 QRemoteObjectHost::QRemoteObjectHost(QObject *parent)
1674     : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1675 { }
1676 
1677 /*!
1678     Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1679     exposing \l Source objects on the QtRO network) with address \a address. If
1680     set, \a registryAddress will be used to connect to the \l
1681     QRemoteObjectRegistry at the provided address. The \a allowedSchemas
1682     parameter is only needed (and should be set to \l
1683     {QRemoteObjectHostBase::AllowExternalRegistration}
1684     {AllowExternalRegistration}) if the schema of the url should be used as an
1685     \l {External Schemas} {External Schema} by the registry.
1686 
1687     \sa setHostUrl(), setRegistryUrl()
1688 */
QRemoteObjectHost(const QUrl & address,const QUrl & registryAddress,AllowedSchemas allowedSchemas,QObject * parent)1689 QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, const QUrl &registryAddress,
1690                                      AllowedSchemas allowedSchemas, QObject *parent)
1691     : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1692 {
1693     if (!address.isEmpty()) {
1694         if (!setHostUrl(address, allowedSchemas))
1695             return;
1696     }
1697 
1698     if (!registryAddress.isEmpty())
1699         setRegistryUrl(registryAddress);
1700 }
1701 
1702 /*!
1703     Constructs a new QRemoteObjectHost Node (i.e., a Node that supports
1704     exposing \l Source objects on the QtRO network) with a url of \a
1705     address and the given \a parent. This overload is provided as a convenience for specifying a
1706     QObject parent without providing a registry address.
1707 
1708     \sa setHostUrl(), setRegistryUrl()
1709 */
QRemoteObjectHost(const QUrl & address,QObject * parent)1710 QRemoteObjectHost::QRemoteObjectHost(const QUrl &address, QObject *parent)
1711     : QRemoteObjectHostBase(*new QRemoteObjectHostPrivate, parent)
1712 {
1713     if (!address.isEmpty())
1714         setHostUrl(address);
1715 }
1716 
1717 /*!
1718     \internal QRemoteObjectHost::QRemoteObjectHost
1719 */
QRemoteObjectHost(QRemoteObjectHostPrivate & d,QObject * parent)1720 QRemoteObjectHost::QRemoteObjectHost(QRemoteObjectHostPrivate &d, QObject *parent)
1721     : QRemoteObjectHostBase(d, parent)
1722 { }
1723 
~QRemoteObjectHost()1724 QRemoteObjectHost::~QRemoteObjectHost() {}
1725 
1726 /*!
1727     Constructs a new QRemoteObjectRegistryHost Node with the given \a parent. RegistryHost Nodes have
1728     the same functionality as \l QRemoteObjectHost Nodes, except rather than
1729     being able to connect to a \l QRemoteObjectRegistry, the provided Host QUrl
1730     (\a registryAddress) becomes the address of the registry for other Nodes to
1731     connect to.
1732 */
QRemoteObjectRegistryHost(const QUrl & registryAddress,QObject * parent)1733 QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(const QUrl &registryAddress, QObject *parent)
1734     : QRemoteObjectHostBase(*new QRemoteObjectRegistryHostPrivate, parent)
1735 {
1736     if (registryAddress.isEmpty())
1737         return;
1738 
1739     setRegistryUrl(registryAddress);
1740 }
1741 
1742 /*!
1743     \internal
1744 */
QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate & d,QObject * parent)1745 QRemoteObjectRegistryHost::QRemoteObjectRegistryHost(QRemoteObjectRegistryHostPrivate &d, QObject *parent)
1746     : QRemoteObjectHostBase(d, parent)
1747 { }
1748 
~QRemoteObjectRegistryHost()1749 QRemoteObjectRegistryHost::~QRemoteObjectRegistryHost() {}
1750 
~QRemoteObjectNode()1751 QRemoteObjectNode::~QRemoteObjectNode()
1752 { }
1753 
~QRemoteObjectHostBase()1754 QRemoteObjectHostBase::~QRemoteObjectHostBase()
1755 { }
1756 
1757 /*!
1758     Sets \a name as the internal name for this Node.  This
1759     is then output as part of the logging (if enabled).
1760     This is primarily useful if you merge log data from multiple nodes.
1761 */
setName(const QString & name)1762 void QRemoteObjectNode::setName(const QString &name)
1763 {
1764     setObjectName(name);
1765 }
1766 
1767 /*!
1768     Similar to QObject::setObjectName() (which this method calls), but this
1769     version also applies the \a name to internal classes as well, which are
1770     used in some of the debugging output.
1771 */
setName(const QString & name)1772 void QRemoteObjectHostBase::setName(const QString &name)
1773 {
1774     Q_D(QRemoteObjectHostBase);
1775     setObjectName(name);
1776     if (d->remoteObjectIo)
1777         d->remoteObjectIo->setObjectName(name);
1778 }
1779 
1780 /*!
1781     \internal The HostBase version of this method is protected so the method
1782     isn't exposed on RegistryHost nodes.
1783 */
hostUrl() const1784 QUrl QRemoteObjectHostBase::hostUrl() const
1785 {
1786     Q_D(const QRemoteObjectHostBase);
1787     return d->remoteObjectIo->serverAddress();
1788 }
1789 
1790 /*!
1791     \internal The HostBase version of this method is protected so the method
1792     isn't exposed on RegistryHost nodes.
1793 */
setHostUrl(const QUrl & hostAddress,AllowedSchemas allowedSchemas)1794 bool QRemoteObjectHostBase::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
1795 {
1796     Q_D(QRemoteObjectHostBase);
1797     if (d->remoteObjectIo) {
1798         d->setLastError(ServerAlreadyCreated);
1799         return false;
1800     }
1801 
1802     if (allowedSchemas == AllowedSchemas::BuiltInSchemasOnly && !QtROServerFactory::instance()->isValid(hostAddress)) {
1803         d->setLastError(HostUrlInvalid);
1804         return false;
1805     }
1806 
1807     if (allowedSchemas == AllowedSchemas::AllowExternalRegistration && QtROServerFactory::instance()->isValid(hostAddress)) {
1808         qWarning() << qPrintable(objectName()) << "Overriding a valid QtRO url (" << hostAddress << ") with AllowExternalRegistration is not allowed.";
1809         d->setLastError(HostUrlInvalid);
1810         return false;
1811     }
1812     d->remoteObjectIo = new QRemoteObjectSourceIo(hostAddress, this);
1813 
1814     if (allowedSchemas == AllowedSchemas::BuiltInSchemasOnly && !d->remoteObjectIo->startListening()) {
1815         d->setLastError(ListenFailed);
1816         delete d->remoteObjectIo;
1817         d->remoteObjectIo = nullptr;
1818         return false;
1819     }
1820 
1821     //If we've given a name to the node, set it on the sourceIo as well
1822     if (!objectName().isEmpty())
1823         d->remoteObjectIo->setObjectName(objectName());
1824     //Since we don't know whether setHostUrl or setRegistryUrl/setRegistryHost will be called first,
1825     //break it into two pieces.  setHostUrl connects the RemoteObjectSourceIo->[add/remove]RemoteObjectSource to QRemoteObjectReplicaNode->[add/remove]RemoteObjectSource
1826     //setRegistry* calls appropriately connect RemoteObjecSourcetIo->[add/remove]RemoteObjectSource to the registry when it is created
1827     QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectAdded, this, &QRemoteObjectHostBase::remoteObjectAdded);
1828     QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::remoteObjectRemoved, this, &QRemoteObjectHostBase::remoteObjectRemoved);
1829 
1830     return true;
1831 }
1832 
1833 /*!
1834     \qmlproperty url Host::hostUrl
1835 
1836     The host address for the node.
1837 
1838     This is the address where source objects remoted by this node will reside.
1839 */
1840 
1841 /*!
1842     \property QRemoteObjectHost::hostUrl
1843     \brief The host address for the node.
1844 
1845     This is the address where source objects remoted by this node will reside.
1846 */
1847 
1848 /*!
1849     Returns the host address for the QRemoteObjectNode as a QUrl. If the Node
1850     is not a Host node, returns an empty QUrl.
1851 
1852     \sa setHostUrl()
1853 */
hostUrl() const1854 QUrl QRemoteObjectHost::hostUrl() const
1855 {
1856     return QRemoteObjectHostBase::hostUrl();
1857 }
1858 
1859 /*!
1860     Sets the \a hostAddress for a host QRemoteObjectNode.
1861 
1862     Returns \c true if the Host address is set, otherwise \c false.
1863 
1864     The \a allowedSchemas parameter is only needed (and should be set to \l
1865     {QRemoteObjectHostBase::AllowExternalRegistration}
1866     {AllowExternalRegistration}) if the schema of the url should be used as an
1867     \l {External Schemas} {External Schema} by the registry.
1868 */
setHostUrl(const QUrl & hostAddress,AllowedSchemas allowedSchemas)1869 bool QRemoteObjectHost::setHostUrl(const QUrl &hostAddress, AllowedSchemas allowedSchemas)
1870 {
1871     bool success = QRemoteObjectHostBase::setHostUrl(hostAddress, allowedSchemas);
1872     if (success)
1873         emit hostUrlChanged();
1874     return success;
1875 }
1876 
1877 /*!
1878     This method can be used to set the address of this Node to \a registryUrl
1879     (used for other Nodes to connect to this one), if the QUrl isn't set in the
1880     constructor. Since this Node becomes the Registry, calling this setter
1881     method causes this Node to use the url as the host address. All other
1882     Node's use the \l {QRemoteObjectNode::setRegistryUrl} method initiate a
1883     connection to the Registry.
1884 
1885     Returns \c true if the registry address is set, otherwise \c false.
1886 
1887     \sa QRemoteObjectRegistryHost(), QRemoteObjectNode::setRegistryUrl
1888 */
setRegistryUrl(const QUrl & registryUrl)1889 bool QRemoteObjectRegistryHost::setRegistryUrl(const QUrl &registryUrl)
1890 {
1891     Q_D(QRemoteObjectRegistryHost);
1892     if (setHostUrl(registryUrl)) {
1893         if (!d->remoteObjectIo) {
1894             d->setLastError(ServerAlreadyCreated);
1895             return false;
1896         } else if (d->registry) {
1897             d->setLastError(RegistryAlreadyHosted);
1898             return false;
1899         }
1900 
1901         QRegistrySource *remoteObject = new QRegistrySource(this);
1902         enableRemoting(remoteObject);
1903         d->registryAddress = d->remoteObjectIo->serverAddress();
1904         d->registrySource = remoteObject;
1905         //Connect RemoteObjectSourceIo->remoteObject[Added/Removde] to the registry Slot
1906         QObject::connect(this, &QRemoteObjectRegistryHost::remoteObjectAdded, d->registrySource, &QRegistrySource::addSource);
1907         QObject::connect(this, &QRemoteObjectRegistryHost::remoteObjectRemoved, d->registrySource, &QRegistrySource::removeSource);
1908         QObject::connect(d->remoteObjectIo, &QRemoteObjectSourceIo::serverRemoved,d->registrySource, &QRegistrySource::removeServer);
1909         //onAdd/Remove update the known remoteObjects list in the RegistrySource, so no need to connect to the RegistrySource remoteObjectAdded/Removed signals
1910         d->setRegistry(acquire<QRemoteObjectRegistry>());
1911         return true;
1912     }
1913     return false;
1914 }
1915 
1916 /*!
1917     Returns the last error set.
1918 */
lastError() const1919 QRemoteObjectNode::ErrorCode QRemoteObjectNode::lastError() const
1920 {
1921     Q_D(const QRemoteObjectNode);
1922     return d->lastError;
1923 }
1924 
1925 /*!
1926     \qmlproperty url Node::registryUrl
1927 
1928     The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
1929 
1930     This is an empty QUrl if there is no registry in use.
1931 */
1932 
1933 /*!
1934     \property QRemoteObjectNode::registryUrl
1935     \brief The address of the \l {QRemoteObjectRegistry} {Registry} used by this node.
1936 
1937     This is an empty QUrl if there is no registry in use.
1938 */
registryUrl() const1939 QUrl QRemoteObjectNode::registryUrl() const
1940 {
1941     Q_D(const QRemoteObjectNode);
1942     return d->registryAddress;
1943 }
1944 
setRegistryUrl(const QUrl & registryAddress)1945 bool QRemoteObjectNode::setRegistryUrl(const QUrl &registryAddress)
1946 {
1947     Q_D(QRemoteObjectNode);
1948     if (d->registry) {
1949         d->setLastError(RegistryAlreadyHosted);
1950         return false;
1951     }
1952 
1953     d->registryAddress = registryAddress;
1954     d->setRegistry(acquire<QRemoteObjectRegistry>());
1955     //Connect remoteObject[Added/Removed] to the registry Slot
1956     QObject::connect(this, &QRemoteObjectNode::remoteObjectAdded, d->registry, &QRemoteObjectRegistry::addSource);
1957     QObject::connect(this, &QRemoteObjectNode::remoteObjectRemoved, d->registry, &QRemoteObjectRegistry::removeSource);
1958     connectToNode(registryAddress);
1959     return true;
1960 }
1961 
setRegistry(QRemoteObjectRegistry * reg)1962 void QRemoteObjectNodePrivate::setRegistry(QRemoteObjectRegistry *reg)
1963 {
1964     Q_Q(QRemoteObjectNode);
1965     registry = reg;
1966     reg->setParent(q);
1967     //Make sure when we get the registry initialized, we update our replicas
1968     QObject::connect(reg, &QRemoteObjectRegistry::initialized, q, [this]() {
1969         onRegistryInitialized();
1970     });
1971     //Make sure we handle new RemoteObjectSources on Registry...
1972     QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectAdded,
1973                      q, [this](const QRemoteObjectSourceLocation &location) {
1974         onRemoteObjectSourceAdded(location);
1975     });
1976     QObject::connect(reg, &QRemoteObjectRegistry::remoteObjectRemoved,
1977                      q, [this](const QRemoteObjectSourceLocation &location) {
1978         onRemoteObjectSourceRemoved(location);
1979     });
1980 }
1981 
handlePointerToQObjectProperty(QConnectedReplicaImplementation * rep,int index,const QVariant & property)1982 QVariant QRemoteObjectNodePrivate::handlePointerToQObjectProperty(QConnectedReplicaImplementation *rep, int index, const QVariant &property)
1983 {
1984     Q_Q(QRemoteObjectNode);
1985     using namespace QRemoteObjectPackets;
1986 
1987     QVariant retval;
1988 
1989     Q_ASSERT(property.canConvert<QRO_>());
1990     QRO_ childInfo = property.value<QRO_>();
1991     qROPrivDebug() << "QRO_:" << childInfo.name << replicas.contains(childInfo.name) << replicas.keys();
1992     if (childInfo.isNull) {
1993         // Either the source has changed the pointer and we need to update it, or the source pointer is a nullptr
1994         if (replicas.contains(childInfo.name))
1995             replicas.remove(childInfo.name);
1996         if (childInfo.type == ObjectType::CLASS)
1997             retval = QVariant::fromValue<QRemoteObjectDynamicReplica*>(nullptr);
1998         else
1999             retval = QVariant::fromValue<QAbstractItemModelReplica*>(nullptr);
2000         return retval;
2001     }
2002 
2003     const bool newReplica = !replicas.contains(childInfo.name) || rep->isInitialized();
2004     if (newReplica) {
2005         if (rep->isInitialized()) {
2006             auto childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.take(childInfo.name));
2007             if (childRep && !childRep->isShortCircuit()) {
2008                 qCDebug(QT_REMOTEOBJECT) << "Checking if dynamic type should be added to dynamicTypeManager (type =" << childRep->m_metaObject->className() << ")";
2009                 dynamicTypeManager.addFromMetaObject(childRep->m_metaObject);
2010             }
2011         }
2012         if (childInfo.type == ObjectType::CLASS)
2013             retval = QVariant::fromValue(q->acquireDynamic(childInfo.name));
2014         else
2015             retval = QVariant::fromValue(q->acquireModel(childInfo.name));
2016     } else //We are receiving the initial data for the QObject
2017         retval = rep->getProperty(index); //Use existing value so changed signal isn't emitted
2018 
2019     QSharedPointer<QConnectedReplicaImplementation> childRep = qSharedPointerCast<QConnectedReplicaImplementation>(replicas.value(childInfo.name).toStrongRef());
2020     if (childRep->connectionToSource.isNull())
2021         childRep->connectionToSource = rep->connectionToSource;
2022     QVariantList parameters;
2023     QDataStream ds(childInfo.parameters);
2024     if (childRep->needsDynamicInitialization()) {
2025         if (childInfo.classDefinition.isEmpty()) {
2026             auto typeName = childInfo.typeName;
2027             if (typeName == QLatin1String("QObject")) {
2028                 // The sender would have included the class name if needed
2029                 // So the acquire must have been templated, and we have the typeName
2030                 typeName = QString::fromLatin1(rep->getProperty(index).typeName());
2031                 if (typeName.endsWith(QLatin1String("Replica*")))
2032                     typeName.chop(8);
2033             }
2034             childRep->setDynamicMetaObject(dynamicTypeManager.metaObjectForType(typeName));
2035         } else {
2036             QDataStream in(childInfo.classDefinition);
2037             childRep->setDynamicMetaObject(dynamicTypeManager.addDynamicType(rep->connectionToSource, in));
2038         }
2039         if (!childInfo.parameters.isEmpty())
2040             ds >> parameters;
2041         handlePointerToQObjectProperties(childRep.data(), parameters);
2042         childRep->setDynamicProperties(parameters);
2043     } else {
2044         if (!childInfo.parameters.isEmpty())
2045             ds >> parameters;
2046         handlePointerToQObjectProperties(childRep.data(), parameters);
2047         childRep->initialize(parameters);
2048     }
2049 
2050     return retval;
2051 }
2052 
handlePointerToQObjectProperties(QConnectedReplicaImplementation * rep,QVariantList & properties)2053 void QRemoteObjectNodePrivate::handlePointerToQObjectProperties(QConnectedReplicaImplementation *rep, QVariantList &properties)
2054 {
2055     for (const int index : rep->childIndices())
2056         properties[index] = handlePointerToQObjectProperty(rep, index, properties.at(index));
2057 }
2058 
2059 /*!
2060     Blocks until this Node's \l Registry is initialized or \a timeout (in
2061     milliseconds) expires. Returns \c true if the \l Registry is successfully
2062     initialized upon return, or \c false otherwise.
2063 */
waitForRegistry(int timeout)2064 bool QRemoteObjectNode::waitForRegistry(int timeout)
2065 {
2066     Q_D(QRemoteObjectNode);
2067     if (!d->registry) {
2068         qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "waitForRegistry() error: No valid registry url set";
2069         return false;
2070     }
2071 
2072     return d->registry->waitForSource(timeout);
2073 }
2074 
2075 /*!
2076     Connects a client node to the host node at \a address.
2077 
2078     Connections will remain valid until the host node is deleted or no longer
2079     accessible over a network.
2080 
2081     Once a client is connected to a host, valid Replicas can then be acquired
2082     if the corresponding Source is being remoted.
2083 
2084     Return \c true on success, \c false otherwise (usually an unrecognized url,
2085     or connecting to already connected address).
2086 */
connectToNode(const QUrl & address)2087 bool QRemoteObjectNode::connectToNode(const QUrl &address)
2088 {
2089     Q_D(QRemoteObjectNode);
2090     if (!d->initConnection(address)) {
2091         d->setLastError(RegistryNotAcquired);
2092         return false;
2093     }
2094     return true;
2095 }
2096 
2097 /*!
2098     \since 5.12
2099 
2100     In order to \l QRemoteObjectNode::acquire() \l Replica objects over \l
2101     {External QIODevices}, Qt Remote Objects needs access to the communications
2102     channel (a \l QIODevice) between the respective nodes. It is the
2103     addClientSideConnection() call that enables this, taking the \a ioDevice as
2104     input. Any acquire() call made without calling addClientSideConnection will
2105     still work, but the Node will not be able to initialize the \l Replica
2106     without being provided the connection to the Host node.
2107 
2108     \sa {QRemoteObjectHostBase::addHostSideConnection}
2109 */
addClientSideConnection(QIODevice * ioDevice)2110 void QRemoteObjectNode::addClientSideConnection(QIODevice *ioDevice)
2111 {
2112     Q_D(QRemoteObjectNode);
2113     ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
2114     connect(device, &IoDeviceBase::readyRead, this, [d, device]() {
2115         d->onClientRead(device);
2116     });
2117     if (device->bytesAvailable())
2118         d->onClientRead(device);
2119 }
2120 
2121 /*!
2122     \fn void QRemoteObjectNode::remoteObjectAdded(const QRemoteObjectSourceLocation &loc)
2123 
2124     This signal is emitted whenever a new \l {Source} object is added to
2125     the Registry. The signal will not be emitted if there is no Registry set
2126     (i.e., Sources over connections made via connectToNode directly). The \a
2127     loc parameter contains the information about the added Source, including
2128     name, type and the QUrl of the hosting Node.
2129 
2130     \sa remoteObjectRemoved(), instances()
2131 */
2132 
2133 /*!
2134     \fn void QRemoteObjectNode::remoteObjectRemoved(const QRemoteObjectSourceLocation &loc)
2135 
2136     This signal is emitted whenever there is a known \l {Source} object is
2137     removed from the Registry. The signal will not be emitted if there is no
2138     Registry set (i.e., Sources over connections made via connectToNode
2139     directly). The \a loc parameter contains the information about the removed
2140     Source, including name, type and the QUrl of the hosting Node.
2141 
2142     \sa remoteObjectAdded, instances
2143 */
2144 
2145 /*!
2146     \fn QStringList QRemoteObjectNode::instances() const
2147 
2148     This templated function (taking a \l repc generated type as the template parameter) will
2149     return the list of names of every instance of that type on the Remote
2150     Objects network. For example, if you have a Shape class defined in a .rep file,
2151     and Circle and Square classes inherit from the Source definition, they can
2152     be shared on the Remote Objects network using \l {QRemoteObjectHostBase::enableRemoting} {enableRemoting}.
2153     \code
2154         Square square;
2155         Circle circle;
2156         myHost.enableRemoting(&square, "Square");
2157         myHost.enableRemoting(&circle, "Circle");
2158     \endcode
2159     Then instance can be used to find the available instances of Shape.
2160     \code
2161         QStringList instances = clientNode.instances<Shape>();
2162         // will return a QStringList containing "Circle" and "Square"
2163         auto instance1 = clientNode.acquire<Shape>("Circle");
2164         auto instance2 = clientNode.acquire<Shape>("Square");
2165         ...
2166     \endcode
2167 */
2168 
2169 /*!
2170     \overload instances()
2171 
2172     This convenience function provides the same result as the templated
2173     version, but takes the name of the \l {Source} class as a parameter (\a
2174     typeName) rather than deriving it from the class type.
2175 */
instances(const QString & typeName) const2176 QStringList QRemoteObjectNode::instances(const QString &typeName) const
2177 {
2178     Q_D(const QRemoteObjectNode);
2179     QStringList names;
2180     for (auto it = d->connectedSources.cbegin(), end = d->connectedSources.cend(); it != end; ++it) {
2181         if (it.value().typeName == typeName) {
2182             names << it.key();
2183         }
2184     }
2185     return names;
2186 }
2187 
2188 /*!
2189     \keyword dynamic acquire
2190     Returns a QRemoteObjectDynamicReplica of the Source \a name.
2191 */
acquireDynamic(const QString & name)2192 QRemoteObjectDynamicReplica *QRemoteObjectNode::acquireDynamic(const QString &name)
2193 {
2194     return new QRemoteObjectDynamicReplica(this, name);
2195 }
2196 
2197 /*!
2198     \qmlmethod bool Host::enableRemoting(object object, string name)
2199     Enables a host node to dynamically provide remote access to the QObject \a
2200     object. Client nodes connected to the node hosting this object may obtain
2201     Replicas of this Source.
2202 
2203     The optional \a name defines the lookup-name under which the QObject can be acquired
2204     using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
2205     given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
2206     was defined for the QObject then the \l QObject::objectName() is used.
2207 
2208     Returns \c false if the current node is a client node, or if the QObject is already
2209     registered to be remoted, and \c true if remoting is successfully enabled
2210     for the dynamic QObject.
2211 
2212     \sa disableRemoting()
2213 */
2214 
2215 /*!
2216     Enables a host node to dynamically provide remote access to the QObject \a
2217     object. Client nodes connected to the node
2218     hosting this object may obtain Replicas of this Source.
2219 
2220     The optional \a name defines the lookup-name under which the QObject can be acquired
2221     using \l QRemoteObjectNode::acquire() . If not explicitly set then the name
2222     given in the QCLASSINFO_REMOTEOBJECT_TYPE will be used. If no such macro
2223     was defined for the QObject then the \l QObject::objectName() is used.
2224 
2225     Returns \c false if the current node is a client node, or if the QObject is already
2226     registered to be remoted, and \c true if remoting is successfully enabled
2227     for the dynamic QObject.
2228 
2229     \sa disableRemoting()
2230 */
enableRemoting(QObject * object,const QString & name)2231 bool QRemoteObjectHostBase::enableRemoting(QObject *object, const QString &name)
2232 {
2233     Q_D(QRemoteObjectHostBase);
2234     if (!d->remoteObjectIo) {
2235         d->setLastError(OperationNotValidOnClientNode);
2236         return false;
2237     }
2238 
2239     const QMetaObject *meta = object->metaObject();
2240     QString _name = name;
2241     QString typeName = getTypeNameAndMetaobjectFromClassInfo(meta);
2242     if (typeName.isEmpty()) { //This is a passed in QObject, use its API
2243         if (_name.isEmpty()) {
2244             _name = object->objectName();
2245             if (_name.isEmpty()) {
2246                 d->setLastError(MissingObjectName);
2247                 qCWarning(QT_REMOTEOBJECT) << qPrintable(objectName()) << "enableRemoting() Error: Unable to Replicate an object that does not have objectName() set.";
2248                 return false;
2249             }
2250         }
2251     } else if (_name.isEmpty())
2252         _name = typeName;
2253     return d->remoteObjectIo->enableRemoting(object, meta, _name, typeName);
2254 }
2255 
2256 /*!
2257     This overload of enableRemoting() is specific to \l QAbstractItemModel types
2258     (or any type derived from \l QAbstractItemModel). This is useful if you want
2259     to have a model and the HMI for the model in different processes.
2260 
2261     The three required parameters are the \a model itself, the \a name by which
2262     to lookup the model, and the \a roles that should be exposed on the Replica
2263     side. If you want to synchronize selection between \l Source and \l
2264     Replica, the optional \a selectionModel parameter can be used. This is only
2265     recommended when using a single Replica.
2266 
2267     Behind the scenes, Qt Remote Objects batches data() lookups and prefetches
2268     data when possible to make the model interaction as responsive as possible.
2269 
2270     Returns \c false if the current node is a client node, or if the QObject is already
2271     registered to be remoted, and \c true if remoting is successfully enabled
2272     for the QAbstractItemModel.
2273 
2274     \sa disableRemoting()
2275  */
enableRemoting(QAbstractItemModel * model,const QString & name,const QVector<int> roles,QItemSelectionModel * selectionModel)2276 bool QRemoteObjectHostBase::enableRemoting(QAbstractItemModel *model, const QString &name, const QVector<int> roles, QItemSelectionModel *selectionModel)
2277 {
2278     //This looks complicated, but hopefully there is a way to have an adapter be a template
2279     //parameter and this makes sure that is supported.
2280     QObject *adapter = QAbstractItemModelSourceAdapter::staticMetaObject.newInstance(Q_ARG(QAbstractItemModel*, model),
2281                                                                                      Q_ARG(QItemSelectionModel*, selectionModel),
2282                                                                                      Q_ARG(QVector<int>, roles));
2283     QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter> *api =
2284         new QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter>(name);
2285     if (!this->objectName().isEmpty())
2286         adapter->setObjectName(this->objectName().append(QLatin1String("Adapter")));
2287     return enableRemoting(model, api, adapter);
2288 }
2289 
2290 /*!
2291     \fn template <template <typename> class ApiDefinition, typename ObjectType> bool QRemoteObjectHostBase::enableRemoting(ObjectType *object)
2292 
2293     This templated function overload enables a host node to provide remote
2294     access to a QObject \a object with a specified (and compile-time checked)
2295     interface. Client nodes connected to the node hosting this object may
2296     obtain Replicas of this Source.
2297 
2298     This is best illustrated by example:
2299     \code
2300         #include "rep_TimeModel_source.h"
2301         MinuteTimer timer;
2302         hostNode.enableRemoting<MinuteTimerSourceAPI>(&timer);
2303     \endcode
2304 
2305     Here the MinuteTimerSourceAPI is the set of Signals/Slots/Properties
2306     defined by the TimeModel.rep file. Compile time checks are made to verify
2307     the input QObject can expose the requested API, it will fail to compile
2308     otherwise. This allows a subset of \a object 's interface to be exposed,
2309     and allows the types of conversions supported by Signal/Slot connections.
2310 
2311     Returns \c false if the current node is a client node, or if the QObject is
2312     already registered to be remoted, and \c true if remoting is successfully
2313     enabled for the QObject.
2314 
2315     \sa disableRemoting()
2316 */
2317 
2318 /*!
2319     \internal
2320     Enables a host node to provide remote access to a QObject \a object
2321     with the API defined by \a api. Client nodes connected to the node
2322     hosting this object may obtain Replicas of this Source.
2323 
2324     Returns \c false if the current node is a client node, or if the QObject is
2325     already registered to be remoted, and \c true if remoting is successfully
2326     enabled for the QObject.
2327 
2328     \sa disableRemoting()
2329 */
enableRemoting(QObject * object,const SourceApiMap * api,QObject * adapter)2330 bool QRemoteObjectHostBase::enableRemoting(QObject *object, const SourceApiMap *api, QObject *adapter)
2331 {
2332     Q_D(QRemoteObjectHostBase);
2333     return d->remoteObjectIo->enableRemoting(object, api, adapter);
2334 }
2335 
2336 /*!
2337     \qmlmethod bool Host::disableRemoting(object remoteObject)
2338     Disables remote access for the QObject \a remoteObject. Returns \c false if
2339     the current node is a client node or if the \a remoteObject is not
2340     registered, and returns \c true if remoting is successfully disabled for
2341     the Source object.
2342 
2343     \warning Replicas of this object will no longer be valid after calling this method.
2344 
2345     \sa enableRemoting()
2346 */
2347 
2348 /*!
2349     Disables remote access for the QObject \a remoteObject. Returns \c false if
2350     the current node is a client node or if the \a remoteObject is not
2351     registered, and returns \c true if remoting is successfully disabled for
2352     the Source object.
2353 
2354     \warning Replicas of this object will no longer be valid after calling this method.
2355 
2356     \sa enableRemoting()
2357 */
disableRemoting(QObject * remoteObject)2358 bool QRemoteObjectHostBase::disableRemoting(QObject *remoteObject)
2359 {
2360     Q_D(QRemoteObjectHostBase);
2361     if (!d->remoteObjectIo) {
2362         d->setLastError(OperationNotValidOnClientNode);
2363         return false;
2364     }
2365 
2366     if (!d->remoteObjectIo->disableRemoting(remoteObject)) {
2367         d->setLastError(SourceNotRegistered);
2368         return false;
2369     }
2370 
2371     return true;
2372 }
2373 
2374 /*!
2375     \since 5.12
2376 
2377     In order to \l QRemoteObjectHost::enableRemoting() \l Source objects over
2378     \l {External QIODevices}, Qt Remote Objects needs access to the
2379     communications channel (a \l QIODevice) between the respective nodes. It is
2380     the addHostSideConnection() call that enables this on the \l Source side,
2381     taking the \a ioDevice as input. Any enableRemoting() call will still work
2382     without calling addHostSideConnection, but the Node will not be able to
2383     share the \l Source objects without being provided the connection to
2384     the Replica node. Before calling this function you must call
2385     \l {QRemoteObjectHost::}{setHostUrl}() with a unique URL and
2386     \l {QRemoteObjectHost::}{AllowExternalRegistration}.
2387 
2388     \sa addClientSideConnection
2389 */
addHostSideConnection(QIODevice * ioDevice)2390 void QRemoteObjectHostBase::addHostSideConnection(QIODevice *ioDevice)
2391 {
2392     Q_D(QRemoteObjectHostBase);
2393     if (!d->remoteObjectIo)
2394         d->remoteObjectIo = new QRemoteObjectSourceIo(this);
2395     ExternalIoDevice *device = new ExternalIoDevice(ioDevice, this);
2396     return d->remoteObjectIo->newConnection(device);
2397 }
2398 
2399 /*!
2400  Returns a pointer to a Replica which is specifically derived from \l
2401  QAbstractItemModel. The \a name provided must match the name used with the
2402  matching \l {QRemoteObjectHostBase::}{enableRemoting} that put
2403  the Model on the network. The returned model will be empty until it is
2404  initialized with the \l Source.
2405  */
acquireModel(const QString & name,QtRemoteObjects::InitialAction action,const QVector<int> & rolesHint)2406 QAbstractItemModelReplica *QRemoteObjectNode::acquireModel(const QString &name, QtRemoteObjects::InitialAction action, const QVector<int> &rolesHint)
2407 {
2408     QAbstractItemModelReplicaImplementation *rep = acquire<QAbstractItemModelReplicaImplementation>(name);
2409     return new QAbstractItemModelReplica(rep, action, rolesHint);
2410 }
2411 
QRemoteObjectHostBasePrivate()2412 QRemoteObjectHostBasePrivate::QRemoteObjectHostBasePrivate()
2413     : QRemoteObjectNodePrivate()
2414     , remoteObjectIo(nullptr)
2415 { }
2416 
~QRemoteObjectHostBasePrivate()2417 QRemoteObjectHostBasePrivate::~QRemoteObjectHostBasePrivate()
2418 { }
2419 
QRemoteObjectHostPrivate()2420 QRemoteObjectHostPrivate::QRemoteObjectHostPrivate()
2421     : QRemoteObjectHostBasePrivate()
2422 { }
2423 
~QRemoteObjectHostPrivate()2424 QRemoteObjectHostPrivate::~QRemoteObjectHostPrivate()
2425 { }
2426 
QRemoteObjectRegistryHostPrivate()2427 QRemoteObjectRegistryHostPrivate::QRemoteObjectRegistryHostPrivate()
2428     : QRemoteObjectHostBasePrivate()
2429     , registrySource(nullptr)
2430 { }
2431 
~QRemoteObjectRegistryHostPrivate()2432 QRemoteObjectRegistryHostPrivate::~QRemoteObjectRegistryHostPrivate()
2433 { }
2434 
ProxyInfo(QRemoteObjectNode * node,QRemoteObjectHostBase * parent,QRemoteObjectHostBase::RemoteObjectNameFilter filter)2435 ProxyInfo::ProxyInfo(QRemoteObjectNode *node, QRemoteObjectHostBase *parent,
2436                      QRemoteObjectHostBase::RemoteObjectNameFilter filter)
2437     : QObject(parent)
2438     , proxyNode(node)
2439     , parentNode(parent)
2440     , proxyFilter(filter)
2441 {
2442     const auto registry = node->registry();
2443     proxyNode->setObjectName(QString::fromLatin1("_ProxyNode"));
2444 
2445     connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
2446             [this](const QRemoteObjectSourceLocation &entry)
2447     {
2448         this->proxyObject(entry, ProxyDirection::Forward);
2449     });
2450     connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
2451             &ProxyInfo::unproxyObject);
2452     connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
2453         QRemoteObjectSourceLocations locations = registry->sourceLocations();
2454         QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
2455         while (i != locations.constEnd()) {
2456             proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()));
2457             ++i;
2458         }
2459     });
2460 
2461     connect(registry, &QRemoteObjectRegistry::stateChanged, this,
2462             [this](QRemoteObjectRegistry::State state, QRemoteObjectRegistry::State /*oldState*/) {
2463         if (state != QRemoteObjectRegistry::Suspect)
2464             return;
2465         // unproxy all objects
2466         for (ProxyReplicaInfo* info : proxiedReplicas)
2467             disableAndDeleteObject(info);
2468         proxiedReplicas.clear();
2469     });
2470 }
2471 
~ProxyInfo()2472 ProxyInfo::~ProxyInfo() {
2473     for (ProxyReplicaInfo* info : proxiedReplicas)
2474         delete info;
2475 }
2476 
setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)2477 bool ProxyInfo::setReverseProxy(QRemoteObjectHostBase::RemoteObjectNameFilter filter)
2478 {
2479     if (qobject_cast<QRemoteObjectRegistryHost *>(parentNode) == nullptr) {
2480         qWarning() << "Setting up reverseProxy() can only be done on a Registry node.";
2481         return false;
2482     }
2483     const auto registry = parentNode->registry();
2484     this->reverseFilter = filter;
2485 
2486     connect(registry, &QRemoteObjectRegistry::remoteObjectAdded, this,
2487             [this](const QRemoteObjectSourceLocation &entry)
2488     {
2489         this->proxyObject(entry, ProxyDirection::Reverse);
2490     });
2491     connect(registry, &QRemoteObjectRegistry::remoteObjectRemoved, this,
2492             &ProxyInfo::unproxyObject);
2493     connect(registry, &QRemoteObjectRegistry::initialized, this, [registry, this]() {
2494         QRemoteObjectSourceLocations locations = registry->sourceLocations();
2495         QRemoteObjectSourceLocations::const_iterator i = locations.constBegin();
2496         while (i != locations.constEnd()) {
2497             proxyObject(QRemoteObjectSourceLocation(i.key(), i.value()), ProxyDirection::Reverse);
2498             ++i;
2499         }
2500     });
2501 
2502     return true;
2503 }
2504 
proxyObject(const QRemoteObjectSourceLocation & entry,ProxyDirection direction)2505 void ProxyInfo::proxyObject(const QRemoteObjectSourceLocation &entry, ProxyDirection direction)
2506 {
2507     const QString name = entry.first;
2508     const QString typeName = entry.second.typeName;
2509 
2510     if (direction == ProxyDirection::Forward) {
2511         // If we are using the reverse proxy, this can be called when reverse proxy objects are added
2512         // Don't try to proxy those back.  We can detect this because the hosting node will be our proxyNode.
2513         auto host = qobject_cast<QRemoteObjectHost *>(proxyNode);
2514         if (host && entry.second.hostUrl == host->hostUrl())
2515             return;
2516         if (!proxyFilter(name, typeName))
2517             return;
2518         Q_ASSERT(!proxiedReplicas.contains(name));
2519 
2520         qCDebug(QT_REMOTEOBJECT) << "Starting proxy for" << name << "from" << entry.second.hostUrl;
2521 
2522         if (entry.second.typeName == QAIMADAPTER()) {
2523             QAbstractItemModelReplica *rep = proxyNode->acquireModel(name);
2524             proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2525             connect(rep, &QAbstractItemModelReplica::initialized, this,
2526                     [rep, name, this]() { this->parentNode->enableRemoting(rep, name, QVector<int>()); });
2527         } else {
2528             QRemoteObjectDynamicReplica *rep = proxyNode->acquireDynamic(name);
2529             proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2530             connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
2531                     [rep, name, this]() { this->parentNode->enableRemoting(rep, name); });
2532         }
2533     } else {
2534         // If we are using the reverse proxy, this can be called when proxy objects are added
2535         // Don't try to proxy those back.  We can detect this because the hosting node will be the parentNode.
2536         // Since we know the parentNode has to be a RegistryNode for reverse proxy to work, we compare against
2537         // the registryUrl().
2538         if (entry.second.hostUrl == parentNode->registryUrl())
2539             return;
2540         if (!reverseFilter(name, typeName))
2541             return;
2542         Q_ASSERT(!proxiedReplicas.contains(name));
2543 
2544         qCDebug(QT_REMOTEOBJECT) << "Starting reverse proxy for" << name << "from" << entry.second.hostUrl;
2545 
2546         if (entry.second.typeName == QAIMADAPTER()) {
2547             QAbstractItemModelReplica *rep = this->parentNode->acquireModel(name);
2548             proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2549             connect(rep, &QAbstractItemModelReplica::initialized, this,
2550                     [rep, name, this]()
2551             {
2552                 QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2553                 Q_ASSERT(host);
2554                 host->enableRemoting(rep, name, QVector<int>());
2555             });
2556         } else {
2557             QRemoteObjectDynamicReplica *rep = this->parentNode->acquireDynamic(name);
2558             proxiedReplicas.insert(name, new ProxyReplicaInfo{rep, direction});
2559             connect(rep, &QRemoteObjectDynamicReplica::initialized, this,
2560                     [rep, name, this]()
2561             {
2562                 QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2563                 Q_ASSERT(host);
2564                 host->enableRemoting(rep, name);
2565             });
2566         }
2567     }
2568 
2569 }
2570 
unproxyObject(const QRemoteObjectSourceLocation & entry)2571 void ProxyInfo::unproxyObject(const QRemoteObjectSourceLocation &entry)
2572 {
2573     const QString name = entry.first;
2574 
2575     if (proxiedReplicas.contains(name)) {
2576         qCDebug(QT_REMOTEOBJECT)  << "Stopping proxy for" << name;
2577         auto const info = proxiedReplicas.take(name);
2578         disableAndDeleteObject(info);
2579     }
2580 }
2581 
disableAndDeleteObject(ProxyReplicaInfo * info)2582 void ProxyInfo::disableAndDeleteObject(ProxyReplicaInfo* info)
2583 {
2584     if (info->direction == ProxyDirection::Forward)
2585         this->parentNode->disableRemoting(info->replica);
2586     else {
2587         QRemoteObjectHostBase *host = qobject_cast<QRemoteObjectHostBase *>(this->proxyNode);
2588         Q_ASSERT(host);
2589         host->disableRemoting(info->replica);
2590     }
2591     delete info;
2592 }
2593 
2594 
2595 QT_END_NAMESPACE
2596 
2597 #include "moc_qremoteobjectnode.cpp"
2598