1 /*
2 * Stellarium Remote Sync plugin
3 * Copyright (C) 2015 Florian Schaukowitsch
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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 Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20 #include "SyncClientHandlers.hpp"
21 #include "SyncClient.hpp"
22
23 #include "SyncMessages.hpp"
24 #include "StelApp.hpp"
25 #include "StelCore.hpp"
26 #include "StelTranslator.hpp"
27 #include "StelMovementMgr.hpp"
28 #include "StelObserver.hpp"
29 #include "StelObjectMgr.hpp"
30 #include "StelPropertyMgr.hpp"
31
32 using namespace SyncProtocol;
33
ClientHandler()34 ClientHandler::ClientHandler()
35 : client(Q_NULLPTR)
36 {
37 core = StelApp::getInstance().getCore();
38 }
39
ClientHandler(SyncClient * client)40 ClientHandler::ClientHandler(SyncClient *client)
41 : client(client)
42 {
43 Q_ASSERT(client);
44 core = StelApp::getInstance().getCore();
45 }
46
47
ClientErrorHandler(SyncClient * client)48 ClientErrorHandler::ClientErrorHandler(SyncClient *client)
49 : ClientHandler(client)
50 {
51 }
52
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)53 bool ClientErrorHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
54 {
55 ErrorMessage msg;
56 bool ok = msg.deserialize(stream,dataSize);
57 peer.peerLog("Received error message from server: " + msg.message);
58
59 client->emitServerError(msg.message);
60
61 //we don't drop the connection here, we let the remote end do that
62 return ok;
63 }
64
ClientAuthHandler(SyncClient * client)65 ClientAuthHandler::ClientAuthHandler(SyncClient *client)
66 : ClientHandler(client)
67 {
68 connect(this, SIGNAL(authenticated()),client,SIGNAL(connected()));
69 }
70
71
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)72 bool ClientAuthHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
73 {
74 //get message type
75 SyncMessageType type = SyncMessageType(peer.msgHeader.msgType);
76
77 if(type == SERVER_CHALLENGE)
78 {
79 if(peer.isAuthenticated())
80 {
81 //we are already authenticated, another challenge is an error
82 qWarning()<<"[SyncClient] received server challenge when not expecting one";
83 return false;
84 }
85
86 ServerChallenge msg;
87 bool ok = msg.deserialize(stream,dataSize);
88
89 if(!ok)
90 {
91 qWarning()<<"[SyncClient] invalid server challenge received";
92 return false;
93 }
94
95 //check challenge for validity
96 if(msg.protocolVersion != SYNC_PROTOCOL_VERSION)
97 {
98 qWarning()<<"[SyncClient] invalid protocol version, dropping connection";
99 return false;
100 }
101
102 const quint32 expectedPluginVersion = (REMOTESYNC_MAJOR << 16) | (REMOTESYNC_MINOR << 8) | (REMOTESYNC_PATCH);
103 const quint32 expectedStellariumVersion =(STELLARIUM_MAJOR << 16) | (STELLARIUM_MINOR<<8) | (STELLARIUM_PATCH);
104
105 if(expectedPluginVersion != msg.remoteSyncVersion)
106 {
107 //This is only a warning here
108 QString str("[SyncClient] RemoteSync plugin version mismatch! Expected: 0x%1, Got: 0x%2");
109 qWarning()<<str.arg(expectedPluginVersion,0,16).arg(msg.remoteSyncVersion,0,16);
110 }
111 if(expectedStellariumVersion != msg.stellariumVersion)
112 {
113 //This is only a warning here
114 QString str("[SyncClient] Stellarium version mismatch! Expected: 0x%1, Got: 0x%2");
115 qWarning()<<str.arg(expectedStellariumVersion,0,16).arg(msg.stellariumVersion,0,16);
116 }
117
118 qDebug()<<"[SyncClient] Received server challenge, sending response";
119
120 //we have to answer with the response
121 ClientChallengeResponse response;
122 //only need to set this
123 response.clientId = msg.clientId;
124
125 peer.authResponseSent = true;
126 peer.writeMessage(response);
127
128 return true;
129 }
130 else if (type == SERVER_CHALLENGERESPONSEVALID)
131 {
132 //this message has no data body, no need to deserialize
133 if(peer.authResponseSent)
134 {
135 //we authenticated correctly, yay!
136 peer.authenticated = true;
137 qDebug()<<"[SyncClient] Connection authenticated";
138 emit authenticated();
139 return true;
140 }
141 else
142 {
143 //we got a confirmation without sending a response, error
144 qWarning()<<"[SyncClient] Got SERVER_CHALLENGERESPONSEVALID message without awaiting it";
145 return false;
146 }
147 }
148 else
149 {
150 //should never happen except the message type<-->handler config was somehow messed up
151 Q_ASSERT(false);
152 return false;
153 }
154 }
155
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)156 bool ClientAliveHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
157 {
158 Q_UNUSED(peer)
159 Alive p;
160 return p.deserialize(stream,dataSize);
161 }
162
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)163 bool ClientTimeHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
164 {
165 Q_UNUSED(peer)
166 Time msg;
167 bool ok = msg.deserialize(stream, dataSize);
168
169 if(!ok)
170 return false;
171
172 //set time variables, time rate first because it causes a resetSync which we overwrite
173 core->setTimeRate(msg.timeRate);
174 core->setJD(msg.jDay);
175 //This is needed for compensation of network delay. Requires system clocks of client/server to be calibrated to the same values.
176 core->setMilliSecondsOfLastJDUpdate(msg.lastTimeSyncTime);
177
178 return true;
179 }
180
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)181 bool ClientLocationHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
182 {
183 Q_UNUSED(peer)
184 Location msg;
185 bool ok = msg.deserialize(stream,dataSize);
186
187 if(!ok)
188 return false;
189
190 //replicated from StelCore::moveObserverTo
191 if(msg.totalDuration>0.0)
192 {
193 //for optimal results, the network latency should be subtracted from the timeToGo...
194
195 StelLocation curLoc = core->getCurrentLocation();
196 if (core->getCurrentObserver()->isTraveling())
197 {
198 // Avoid using a temporary location name to create another temporary one (otherwise it looks like loc1 -> loc2 -> loc3 etc..)
199 curLoc.name = ".";
200 }
201
202 //create a spaceship observer
203 SpaceShipObserver* newObs = new SpaceShipObserver(curLoc, msg.stelLocation, msg.totalDuration,msg.timeToGo);
204 core->setObserver(newObs);
205 newObs->update(0);
206 }
207 else
208 {
209 //create a normal observer
210 core->setObserver(new StelObserver(msg.stelLocation));
211 }
212 emit core->targetLocationChanged(msg.stelLocation);
213 emit core->locationChanged(core->getCurrentLocation());
214
215
216 return true;
217 }
218
ClientSelectionHandler()219 ClientSelectionHandler::ClientSelectionHandler()
220 {
221 objMgr = &StelApp::getInstance().getStelObjectMgr();
222 }
223
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)224 bool ClientSelectionHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
225 {
226 Q_UNUSED(peer)
227 Selection msg;
228 bool ok = msg.deserialize(stream, dataSize);
229
230 if(!ok)
231 return false;
232
233 qDebug()<<msg;
234
235 //lookup the objects from their names
236 //this might cause problems if 2 objects of different types have the same name!
237 QList<StelObjectP> selection;
238
239 for (const auto& selectedObject : msg.selectedObjects)
240 {
241 StelObjectP obj = objMgr->searchByID(selectedObject.first, selectedObject.second);
242 if(obj)
243 selection.append(obj);
244 else
245 qWarning() << "Object not found" << selectedObject.first << selectedObject.second;
246 }
247
248 if(selection.isEmpty())
249 objMgr->unSelect();
250 else
251 {
252 //set selection
253 objMgr->setSelectedObject(selection,StelModule::ReplaceSelection);
254 }
255
256 return true;
257 }
258
ClientStelPropertyUpdateHandler(bool skipGuiProps,const QStringList & excludeProps)259 ClientStelPropertyUpdateHandler::ClientStelPropertyUpdateHandler(bool skipGuiProps, const QStringList &excludeProps)
260 {
261 propMgr = StelApp::getInstance().getStelPropertyManager();
262
263 QString pattern("^(");
264 //construct a regular expression for the excludes
265 bool first = true;
266 for (auto str : excludeProps)
267 {
268 QString tmp = QRegularExpression::escape(str);
269 // replace escaped asterisks with the regex "all"
270 tmp.replace("\\*",".*");
271 if(!first)
272 {
273 pattern += '|';
274 }
275 first = false;
276 pattern += tmp;
277 }
278
279 if(skipGuiProps)
280 {
281 if(!first)
282 {
283 pattern += '|';
284 }
285 first = false;
286
287 //this is an attempt to filter out the GUI related properties
288 pattern += "(actionShow_.*(Window_Global|_dialog))"; //most dialogs follow one of these patterns
289 pattern += "|actionShow_Scenery3d_storedViewDialog"; //add other dialogs here
290 }
291
292 //finish the pattern
293 pattern += ")$";
294 filter.setPattern(pattern);
295
296 if(!filter.isValid())
297 qWarning()<<"Invalid StelProperty filter:"<<filter.errorString();
298 else
299 qDebug()<<"Constructed regex"<<filter;
300
301 filter.optimize();
302 }
303
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)304 bool ClientStelPropertyUpdateHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
305 {
306 Q_UNUSED(peer)
307 StelPropertyUpdate msg;
308 bool ok = msg.deserialize(stream, dataSize);
309
310 if(!ok)
311 return false;
312
313 qDebug()<<msg;
314
315 QRegularExpressionMatch match = filter.match(msg.propId);
316 if(match.hasMatch())
317 {
318 //filtered property
319 qDebug()<<"Filtered"<<msg;
320 return true;
321 }
322 propMgr->setStelPropertyValue(msg.propId,msg.value);
323 return true;
324 }
325
ClientViewHandler()326 ClientViewHandler::ClientViewHandler()
327 {
328 mvMgr = core->getMovementMgr();
329 }
330
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)331 bool ClientViewHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
332 {
333 Q_UNUSED(peer)
334 View msg;
335 bool ok = msg.deserialize(stream, dataSize);
336 if(!ok) return false;
337
338 mvMgr->setViewDirectionJ2000(core->altAzToJ2000(msg.viewAltAz, StelCore::RefractionOff));
339 return true;
340 }
341
ClientFovHandler()342 ClientFovHandler::ClientFovHandler()
343 {
344 mvMgr = core->getMovementMgr();
345 }
346
handleMessage(QDataStream & stream,SyncProtocol::tPayloadSize dataSize,SyncRemotePeer & peer)347 bool ClientFovHandler::handleMessage(QDataStream &stream, SyncProtocol::tPayloadSize dataSize, SyncRemotePeer &peer)
348 {
349 Q_UNUSED(peer)
350 Fov msg;
351 bool ok = msg.deserialize(stream, dataSize);
352 if(!ok) return false;
353
354 mvMgr->zoomTo(msg.fov, 0.0f);
355 return true;
356 }
357