49033cdf6e4c465dc278fd78e30033564d7b1c86
[quassel.git] / src / common / signalproxy.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #pragma once
22
23 #include "common-export.h"
24
25 #include <functional>
26 #include <initializer_list>
27
28 #include <QEvent>
29 #include <QSet>
30
31 #include "protocol.h"
32
33 struct QMetaObject;
34 class QIODevice;
35
36 class Peer;
37 class SyncableObject;
38
39 class COMMON_EXPORT SignalProxy : public QObject
40 {
41     Q_OBJECT
42
43     class SignalRelay;
44
45 public:
46     enum ProxyMode
47     {
48         Server,
49         Client
50     };
51
52     enum EventType
53     {
54         RemovePeerEvent = QEvent::User
55     };
56
57     SignalProxy(QObject* parent);
58     SignalProxy(ProxyMode mode, QObject* parent);
59     ~SignalProxy() override;
60
61     void setProxyMode(ProxyMode mode);
62     inline ProxyMode proxyMode() const { return _proxyMode; }
63
64     void setHeartBeatInterval(int secs);
65     inline int heartBeatInterval() const { return _heartBeatInterval; }
66     void setMaxHeartBeatCount(int max);
67     inline int maxHeartBeatCount() const { return _maxHeartBeatCount; }
68
69     bool addPeer(Peer* peer);
70
71     bool attachSignal(QObject* sender, const char* signal, const QByteArray& sigName = QByteArray());
72     bool attachSlot(const QByteArray& sigName, QObject* recv, const char* slot);
73
74     void synchronize(SyncableObject* obj);
75     void stopSynchronize(SyncableObject* obj);
76
77     class ExtendedMetaObject;
78     ExtendedMetaObject* extendedMetaObject(const QMetaObject* meta) const;
79     ExtendedMetaObject* createExtendedMetaObject(const QMetaObject* meta, bool checkConflicts = false);
80     inline ExtendedMetaObject* extendedMetaObject(const QObject* obj) const { return extendedMetaObject(metaObject(obj)); }
81     inline ExtendedMetaObject* createExtendedMetaObject(const QObject* obj, bool checkConflicts = false)
82     {
83         return createExtendedMetaObject(metaObject(obj), checkConflicts);
84     }
85
86     bool isSecure() const { return _secure; }
87     void dumpProxyStats();
88     void dumpSyncMap(SyncableObject* object);
89
90     static SignalProxy* current();
91
92     /**@{*/
93     /**
94      * This method allows to send a signal only to a limited set of peers
95      * @param peers A list of peers that should receive it
96      * @param closure Code you want to execute within of that restricted environment
97      */
98     void restrictTargetPeers(QSet<Peer*> peers, std::function<void()> closure);
99     void restrictTargetPeers(Peer* peer, std::function<void()> closure)
100     {
101         QSet<Peer*> set;
102         set.insert(peer);
103         restrictTargetPeers(set, std::move(closure));
104     }
105
106     // A better version, but only implemented on Qt5 if Initializer Lists exist
107 #ifdef Q_COMPILER_INITIALIZER_LISTS
108     void restrictTargetPeers(std::initializer_list<Peer*> peers, std::function<void()> closure)
109     {
110         restrictTargetPeers(QSet<Peer*>(peers), std::move(closure));
111     }
112 #endif
113     /**}@*/
114
115     inline int peerCount() const { return _peerMap.size(); }
116     QVariantList peerData();
117
118     Peer* peerById(int peerId);
119
120     /**
121      * @return If handling a signal, the Peer from which the current signal originates
122      */
123     Peer* sourcePeer();
124     void setSourcePeer(Peer* sourcePeer);
125
126     /**
127      * @return If sending a signal, the Peer to which the current signal is directed
128      */
129     Peer* targetPeer();
130     void setTargetPeer(Peer* targetPeer);
131
132 public slots:
133     void detachObject(QObject* obj);
134     void detachSignals(QObject* sender);
135     void detachSlots(QObject* receiver);
136
137 protected:
138     void customEvent(QEvent* event) override;
139     void sync_call__(const SyncableObject* obj, ProxyMode modeType, const char* funcname, va_list ap);
140     void renameObject(const SyncableObject* obj, const QString& newname, const QString& oldname);
141
142 private slots:
143     void removePeerBySender();
144     void objectRenamed(const QByteArray& classname, const QString& newname, const QString& oldname);
145     void updateSecureState();
146
147 signals:
148     void peerRemoved(Peer* peer);
149     void connected();
150     void disconnected();
151     void objectInitialized(SyncableObject*);
152     void heartBeatIntervalChanged(int secs);
153     void maxHeartBeatCountChanged(int max);
154     void lagUpdated(int lag);
155     void secureStateChanged(bool);
156
157 private:
158     template<class T>
159     class PeerMessageEvent;
160
161     void init();
162     void initServer();
163     void initClient();
164
165     static const QMetaObject* metaObject(const QObject* obj);
166
167     void removePeer(Peer* peer);
168     void removeAllPeers();
169
170     int nextPeerId() { return _lastPeerId++; }
171
172     template<class T>
173     void dispatch(const T& protoMessage);
174     template<class T>
175     void dispatch(Peer* peer, const T& protoMessage);
176
177     void handle(Peer* peer, const Protocol::SyncMessage& syncMessage);
178     void handle(Peer* peer, const Protocol::RpcCall& rpcCall);
179     void handle(Peer* peer, const Protocol::InitRequest& initRequest);
180     void handle(Peer* peer, const Protocol::InitData& initData);
181
182     template<class T>
183     void handle(Peer*, T)
184     {
185         Q_ASSERT(0);
186     }
187
188     bool invokeSlot(QObject* receiver, int methodId, const QVariantList& params, QVariant& returnValue, Peer* peer = nullptr);
189     bool invokeSlot(QObject* receiver, int methodId, const QVariantList& params = QVariantList(), Peer* peer = nullptr);
190
191     void requestInit(SyncableObject* obj);
192     QVariantMap initData(SyncableObject* obj) const;
193     void setInitData(SyncableObject* obj, const QVariantMap& properties);
194
195     static void disconnectDevice(QIODevice* dev, const QString& reason = QString());
196
197     QHash<int, Peer*> _peerMap;
198
199     // containg a list of argtypes for fast access
200     QHash<const QMetaObject*, ExtendedMetaObject*> _extendedMetaObjects;
201
202     // SignalRelay for all manually attached signals
203     SignalRelay* _signalRelay;
204
205     // RPC function -> (object, slot ID)
206     using MethodId = QPair<QObject*, int>;
207     using SlotHash = QMultiHash<QByteArray, MethodId>;
208     SlotHash _attachedSlots;
209
210     // slaves for sync
211     using ObjectId = QHash<QString, SyncableObject*>;
212     QHash<QByteArray, ObjectId> _syncSlave;
213
214     ProxyMode _proxyMode;
215     int _heartBeatInterval;
216     int _maxHeartBeatCount;
217
218     bool _secure;  // determines if all connections are in a secured state (using ssl or internal connections)
219
220     int _lastPeerId = 0;
221
222     QSet<Peer*> _restrictedTargets;
223     bool _restrictMessageTarget = false;
224
225     Peer* _sourcePeer = nullptr;
226     Peer* _targetPeer = nullptr;
227
228     friend class SignalRelay;
229     friend class SyncableObject;
230     friend class Peer;
231 };
232
233 // ==================================================
234 //  ExtendedMetaObject
235 // ==================================================
236 class SignalProxy::ExtendedMetaObject
237 {
238     class MethodDescriptor
239     {
240     public:
241         MethodDescriptor(const QMetaMethod& method);
242         MethodDescriptor() = default;
243
244         inline const QByteArray& methodName() const { return _methodName; }
245         inline const QList<int>& argTypes() const { return _argTypes; }
246         inline int returnType() const { return _returnType; }
247         inline int minArgCount() const { return _minArgCount; }
248         inline SignalProxy::ProxyMode receiverMode() const { return _receiverMode; }
249
250     private:
251         QByteArray _methodName;
252         QList<int> _argTypes;
253         int _returnType{-1};
254         int _minArgCount{-1};
255         SignalProxy::ProxyMode _receiverMode{
256             SignalProxy::Client};  // Only acceptable as a Sync Call if the receiving SignalProxy is in this mode.
257     };
258
259 public:
260     ExtendedMetaObject(const QMetaObject* meta, bool checkConflicts);
261
262     inline const QByteArray& methodName(int methodId) { return methodDescriptor(methodId).methodName(); }
263     inline const QList<int>& argTypes(int methodId) { return methodDescriptor(methodId).argTypes(); }
264     inline int returnType(int methodId) { return methodDescriptor(methodId).returnType(); }
265     inline int minArgCount(int methodId) { return methodDescriptor(methodId).minArgCount(); }
266     inline SignalProxy::ProxyMode receiverMode(int methodId) { return methodDescriptor(methodId).receiverMode(); }
267
268     inline int methodId(const QByteArray& methodName) { return _methodIds.contains(methodName) ? _methodIds[methodName] : -1; }
269
270     inline int updatedRemotelyId() { return _updatedRemotelyId; }
271
272     inline const QHash<QByteArray, int>& slotMap() { return _methodIds; }
273     const QHash<int, int>& receiveMap();
274
275     const QMetaObject* metaObject() const { return _meta; }
276
277     static QByteArray methodName(const QMetaMethod& method);
278     static QString methodBaseName(const QMetaMethod& method);
279
280 private:
281     const MethodDescriptor& methodDescriptor(int methodId);
282
283     const QMetaObject* _meta;
284     int _updatedRemotelyId;  // id of the updatedRemotely() signal - makes things faster
285
286     QHash<int, MethodDescriptor> _methods;
287     QHash<QByteArray, int> _methodIds;
288     QHash<int, int> _receiveMap;  // if slot x is called then hand over the result to slot y
289 };