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