1 /*******************************************************************************************************
2  DkConnection.cpp
3  Created on:	20.07.2011
4 
5  nomacs is a fast and small image viewer with the capability of synchronizing multiple instances
6 
7  Copyright (C) 2011-2013 Markus Diem <markus@nomacs.org>
8  Copyright (C) 2011-2013 Stefan Fiel <stefan@nomacs.org>
9  Copyright (C) 2011-2013 Florian Kleber <florian@nomacs.org>
10 
11  This file is part of nomacs.
12 
13  nomacs is free software: you can redistribute it and/or modify
14  it under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  nomacs is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  GNU General Public License for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with this program.  If not, see <http://www.gnu.org/licenses/>.
25 
26  *******************************************************************************************************/
27 
28 #include "DkConnection.h"
29 #include "DkSettings.h"
30 
31 #pragma warning(push, 0)	// no warnings from includes - begin
32 #include <QBuffer>
33 #include <QByteArray>
34 #include <QTimer>
35 #include <QHostInfo>
36 #include <QThread>
37 #include <QDebug>
38 #pragma warning(pop)		// no warnings from includes - end
39 
40 namespace nmc {
41 
42 // DkConnection --------------------------------------------------------------------
43 
DkConnection(QObject * parent)44 DkConnection::DkConnection(QObject* parent) : QTcpSocket(parent) {
45 
46 	mNumBytesForCurrentDataType = -1;
47 	mIsGreetingMessageSent = false;
48 	mIsSynchronizeMessageSent = false;
49 	connectionCreated = false;
50 	mSynchronizedTimer = new QTimer(this);
51 
52 	connect(mSynchronizedTimer, SIGNAL(timeout()), this, SLOT(synchronizedTimerTimeout()));
53 	connect(this, SIGNAL(readyRead()), this, SLOT(processReadyRead()));
54 
55 	setReadBufferSize(MaxBufferSize);
56 }
57 
setTitle(const QString & newTitle)58 void DkConnection::setTitle(const QString& newTitle) {
59 	mCurrentTitle = newTitle;
60 }
61 
sendStartSynchronizeMessage()62 void DkConnection::sendStartSynchronizeMessage() {
63 	//qDebug() << "sending Synchronize Message to " << this->peerName() << ":" << this->peerPort();
64 	if (mIsSynchronizeMessageSent == false) // initialize sync message, not the response
65 		mSynchronizedTimer->start(1000);
66 
67 	QByteArray ba;
68 	QDataStream ds(&ba, QIODevice::ReadWrite);
69 	ds << quint16(mSynchronizedPeersServerPorts.size());
70 	for (int i = 0; i < mSynchronizedPeersServerPorts.size(); i++) {
71 		qDebug() << "mSynchronizedPeersServerPorts: " << mSynchronizedPeersServerPorts[i];
72 		ds << mSynchronizedPeersServerPorts[i];
73 	}
74 	//QByteArray data = "SYNCHRONIZE" + SeparatorToken + QByteArray::number(synchronize.size()) + SeparatorToken + synchronize;
75 	QByteArray data = "STARTSYNCHRONIZE";
76 	data.append(SeparatorToken).append(QByteArray::number(ba.size())).append(SeparatorToken).append(ba);
77 	qDebug() << "sending startsynchronize:" << data;
78 	if (write(data) == data.size())
79 		mIsSynchronizeMessageSent = true;
80 }
81 
sendStopSynchronizeMessage()82 void DkConnection::sendStopSynchronizeMessage() {
83 	if (mState == Synchronized) { // only send message if connection is synchronized
84 		//qDebug() << "sending disable synchronize Message to " << this->peerName() << ":" << this->peerPort();
85 		QByteArray synchronize = "disable synchronizing";
86 		//QByteArray data = "DISABLESYNCHRONIZE" + SeparatorToken + QByteArray::number(synchronize.size()) + SeparatorToken + synchronize;
87 		QByteArray data = "STOPSYNCHRONIZE";
88 		data.append(SeparatorToken).append(QByteArray::number(synchronize.size())).append(SeparatorToken).append(synchronize);
89 		if (write(data) == data.size())
90 			mIsSynchronizeMessageSent = false;
91 		mState=ReadyForUse;
92 	}
93 }
94 
sendNewTitleMessage(const QString & newtitle)95 void DkConnection::sendNewTitleMessage(const QString& newtitle) {
96 	mCurrentTitle = newtitle;
97 	//qDebug() << "sending new Title (\"" << newtitle << "\") Message to " << this->peerName() << ":" << this->peerPort();
98 
99 	QByteArray newTitleBA=newtitle.toUtf8();
100 	//QByteArray data = "NEWTITLE" + SeparatorToken + QByteArray::number(newTitleBA.size()) + SeparatorToken + newTitleBA;
101 	QByteArray data = "NEWTITLE";
102 	data.append(SeparatorToken).append(QByteArray::number(newTitleBA.size())).append(SeparatorToken).append(newTitleBA);
103 	write(data);
104 }
105 
sendNewPositionMessage(QRect position,bool opacity,bool overlaid)106 void DkConnection::sendNewPositionMessage(QRect position, bool opacity, bool overlaid) {
107 	//qDebug() << "sending new Position to " << this->peerName() << ":" << this->peerPort();
108 	QByteArray ba;
109 	QDataStream ds(&ba, QIODevice::ReadWrite);
110 	ds << position;
111 	ds << opacity;
112 	ds << overlaid;
113 
114 	//QByteArray data = "NEWTITLE" + SeparatorToken + QByteArray::number(ba.size()) + SeparatorToken + ba;
115 	QByteArray data = "NEWPOSITION";
116 	data.append(SeparatorToken).append(QByteArray::number(ba.size())).append(SeparatorToken).append(ba);
117 	write(data);
118 }
119 
sendNewTransformMessage(QTransform transform,QTransform imgTransform,QPointF canvasSize)120 void DkConnection::sendNewTransformMessage(QTransform transform, QTransform imgTransform, QPointF canvasSize) {
121 	//qDebug() << "sending new Transform Message to " << this->peerName() << ":" << this->peerPort();
122 	QByteArray ba;
123 	QDataStream ds(&ba, QIODevice::ReadWrite);
124 	ds << transform;
125 	ds << imgTransform;
126 	ds << canvasSize;
127 
128 	//QByteArray data = "NEWTRANSFORM" + SeparatorToken + QByteArray::number(ba.size()) + SeparatorToken + ba;
129 	QByteArray data = "NEWTRANSFORM";
130 	data.append(SeparatorToken).append(QByteArray::number(ba.size())).append(SeparatorToken).append(ba);
131 	write(data);
132 }
133 
sendNewFileMessage(qint16 op,const QString & filename)134 void DkConnection::sendNewFileMessage(qint16 op, const QString& filename) {
135 	//qDebug() << "sending new File Message to " << this->peerName() << ":" << this->peerPort();
136 	QByteArray ba;
137 	QDataStream ds(&ba, QIODevice::ReadWrite);
138 	ds << op;
139 	ds << filename;
140 	QByteArray data = "NEWFILE";
141 	data.append(SeparatorToken).append(QByteArray::number(ba.size())).append(SeparatorToken).append(ba);
142 	write(data);
143 }
144 
sendNewGoodbyeMessage()145 void DkConnection::sendNewGoodbyeMessage() {
146 	//qDebug() << "sending good bye to " << peerName() << ":" << this->peerPort();
147 
148 	QByteArray ba = "GoodBye";  // scherz?
149 	QByteArray data = "GOODBYE";
150 	data.append(SeparatorToken).append(QByteArray::number(ba.size())).append(SeparatorToken).append(ba);
151 	write(data);
152 	waitForBytesWritten();
153 }
154 
synchronizedPeersListChanged(QList<quint16> newList)155 void DkConnection::synchronizedPeersListChanged(QList<quint16> newList) {
156 	mSynchronizedPeersServerPorts = newList;
157 }
158 
readProtocolHeader()159 bool DkConnection::readProtocolHeader() {
160 	QByteArray greetingBA = QByteArray("GREETING").append(SeparatorToken);
161 	QByteArray synchronizeBA = QByteArray("STARTSYNCHRONIZE").append(SeparatorToken);
162 	QByteArray disableSynchronizeBA = QByteArray("STOPSYNCHRONIZE").append(SeparatorToken);
163 	QByteArray newtitleBA = QByteArray("NEWTITLE").append(SeparatorToken);
164 	QByteArray newtransformBA = QByteArray("NEWTRANSFORM").append(SeparatorToken);
165 	QByteArray newpositionBA = QByteArray("NEWPOSITION").append(SeparatorToken);
166 	QByteArray newFileBA = QByteArray("NEWFILE").append(SeparatorToken);
167 	QByteArray goodbyeBA = QByteArray("GOODBYE").append(SeparatorToken);
168 
169 	if (mBuffer == greetingBA) {
170 		//qDebug() << "Greeting received from:" << this->peerAddress() << ":" << this->peerPort();
171 		mCurrentDataType = Greeting;
172 	} else if (mBuffer == synchronizeBA) {
173 		//qDebug() << "Synchronize received from:" << this->peerAddress() << ":" << this->peerPort();
174 		mCurrentDataType = startSynchronize;
175 	} else if (mBuffer == disableSynchronizeBA) {
176 		//qDebug() << "StopSynchronize received from:" << this->peerAddress() << ":" << this->peerPort();
177 		mCurrentDataType = stopSynchronize;
178 	} else if (mBuffer == newtitleBA) {
179 		//qDebug() << "New Title received from:" << this->peerAddress() << ":" << this->peerPort();
180 		mCurrentDataType = newTitle;
181 	} else if (mBuffer == newtransformBA) {
182 		//qDebug() << "New Transform received from:" << this->peerAddress() << ":" << this->peerPort();
183 		mCurrentDataType = newTransform;
184 	} else if (mBuffer == newpositionBA) {
185 		//qDebug() << "New Position received from:" << this->peerAddress() << ":" << this->peerPort();
186 		mCurrentDataType = newPosition;
187 	} else if (mBuffer == newFileBA) {
188 		//qDebug() << "New File received from:" << this->peerAddress() << ":" << this->peerPort();
189 		mCurrentDataType = newFile;
190 	} else if (mBuffer == goodbyeBA) {
191 		//qDebug() << "Goodbye received from:" << this->peerAddress() << ":" << this->peerPort();
192 		mCurrentDataType = GoodBye;
193 	} else {
194 		qDebug() << QString(mBuffer);
195 		qDebug() << "Undefined received from:" << this->peerAddress() << ":" << this->peerPort();
196 		mCurrentDataType = Undefined;
197 		//abort();
198 		//return false;
199 		return true;
200 	}
201 
202 	mBuffer.clear();
203 	mNumBytesForCurrentDataType = dataLengthForCurrentDataType();
204 	return true;
205 }
206 
readDataIntoBuffer(int maxSize)207 int DkConnection::readDataIntoBuffer(int maxSize) {
208 	if (maxSize > MaxBufferSize)
209 		return 0;
210 
211 	int numBytesBeforeRead = mBuffer.size();
212 	if (numBytesBeforeRead == MaxBufferSize) {
213 		qDebug() << "DkConnection::readDataIntoBuffer: Connection aborted";
214 		abort();
215 		return 0;
216 	}
217 
218 	while (bytesAvailable() > 0 && mBuffer.size() < maxSize) {
219 		mBuffer.append(read(1));
220 		if (mBuffer.endsWith(SeparatorToken)) {
221 			break;
222 		}
223 	}
224 	return mBuffer.size() - numBytesBeforeRead;
225 }
226 
hasEnoughData()227 bool DkConnection::hasEnoughData() {
228 	if (mNumBytesForCurrentDataType <= 0) {
229 		mNumBytesForCurrentDataType = dataLengthForCurrentDataType();
230 	}
231 
232 	//qDebug() << "numBytesForCurrentDataType:" << numBytesForCurrentDataType;
233 	//qDebug() << "bytesAvailable:" << bytesAvailable();
234 	//qDebug() << "buffer size:" << buffer.size();
235 
236 	if (bytesAvailable() < mNumBytesForCurrentDataType || mNumBytesForCurrentDataType <= 0) {
237 		return false;
238 	}
239 
240 	return true;
241 }
242 
dataLengthForCurrentDataType()243 int DkConnection::dataLengthForCurrentDataType() {
244 	if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0 || !mBuffer.endsWith(SeparatorToken))
245 		return 0;
246 
247 	mBuffer.chop(1);
248 	int number = mBuffer.toInt();
249 	mBuffer.clear();
250 	return number;
251 }
252 
processReadyRead()253 void DkConnection::processReadyRead() {
254 	if (readDataIntoBuffer() <= 0)
255 		return;
256 	if (!readProtocolHeader())
257 		return;
258 	checkState();
259 
260 	readWhileBytesAvailable();
261 }
262 
checkState()263 void DkConnection::checkState() {
264 
265 	if (mState == WaitingForGreeting) {
266 		if (mCurrentDataType != Greeting) {
267 			abort();
268 			return;
269 		}
270 
271 		if (!hasEnoughData())
272 			return;
273 
274 		mBuffer = read(mNumBytesForCurrentDataType);
275 		if (mBuffer.size() != mNumBytesForCurrentDataType) {
276 			abort();
277 			return;
278 		}
279 
280 		if (!isValid()) {
281 			abort();
282 			return;
283 		}
284 
285 		if (!mIsGreetingMessageSent)
286 			sendGreetingMessage(mCurrentTitle);
287 
288 		mState = ReadyForUse;
289 		mPortOfPeer = peerPort(); // save peer port ... otherwise connections where this instance is server can not be removed from peerList
290 
291 		readGreetingMessage();
292 
293 		mBuffer.clear();
294 		mNumBytesForCurrentDataType = 0;
295 		mCurrentDataType = Undefined;
296 		return;
297 	}
298 
299 	if (mState==ReadyForUse && mCurrentDataType == startSynchronize) {
300 		if (!hasEnoughData())
301 			return;
302 
303 		mBuffer = read(mNumBytesForCurrentDataType);
304 		if (mBuffer.size() != mNumBytesForCurrentDataType) {
305 			abort();
306 			return;
307 		}
308 
309 		QDataStream ds(mBuffer);
310 		QList<quint16> synchronizedPeersOfOtherInstance;
311 		quint16 numberOfSynchronizedPeers;
312 		ds >> numberOfSynchronizedPeers;
313 
314 		//qDebug() << "other client is sychronized with: ";
315 		for (int i=0; i < numberOfSynchronizedPeers; i++) {
316 			quint16 peerId;
317 			ds >> peerId;
318 			synchronizedPeersOfOtherInstance.push_back(peerId);
319 			//qDebug() << peerId;
320 		}
321 		mCurrentDataType = Undefined;
322 		mNumBytesForCurrentDataType = 0;
323 		mBuffer.clear();
324 
325 		if (!isValid()) {
326 			abort();
327 			return;
328 		}
329 
330 		mState = Synchronized;
331 		if (!mIsSynchronizeMessageSent)
332 			sendStartSynchronizeMessage();
333 
334 		mSynchronizedTimer->stop();
335 		emit connectionStartSynchronize(synchronizedPeersOfOtherInstance, this);
336 		return;
337 	}
338 
339 	if (mState==Synchronized && mCurrentDataType == stopSynchronize) {
340 		mState=ReadyForUse;
341 		this->mIsSynchronizeMessageSent=false;
342 		emit connectionStopSynchronize(this);
343 		mBuffer = read(mNumBytesForCurrentDataType);
344 		if (mBuffer.size() != mNumBytesForCurrentDataType) {
345 			abort();
346 			return;
347 		}
348 
349 		mCurrentDataType = Undefined;
350 		mNumBytesForCurrentDataType = 0;
351 		mBuffer.clear();
352 
353 		return;
354 	}
355 
356 	if (mCurrentDataType == GoodBye) {
357 		//qDebug() << "received GoodBye from " << peerAddress() << ":" << peerPort();
358 		emit connectionGoodBye(this);
359 		mCurrentDataType = Undefined;
360 		mNumBytesForCurrentDataType = 0;
361 		mBuffer.clear();
362 		abort();
363 		return;
364 	}
365 }
366 
readWhileBytesAvailable()367 void DkConnection::readWhileBytesAvailable() {
368 	do {
369 		if (mCurrentDataType == Undefined) {
370 			if (readDataIntoBuffer() <= 0)
371 				return;
372 			if (!readProtocolHeader())
373 				return;
374 			checkState();
375 		}
376 		if (!hasEnoughData()) {
377 			return;
378 		}
379 
380 		mBuffer = read(mNumBytesForCurrentDataType);
381 		if (mBuffer.size() != mNumBytesForCurrentDataType) {
382 			abort();
383 			return;
384 		}
385 		processData();
386 
387 	} while (bytesAvailable() > 0);
388 }
389 
readDataTypeIntoBuffer()390 bool DkConnection::readDataTypeIntoBuffer() {
391 	mBuffer = read(mNumBytesForCurrentDataType);
392 	if (mBuffer.size() != mNumBytesForCurrentDataType) {
393 		abort();
394 		return false;
395 	}
396 	return true;
397 
398 }
399 
processData()400 void DkConnection::processData() {
401 	switch (mCurrentDataType) {
402 	case newTitle:
403 		emit connectionTitleHasChanged(this, QString::fromUtf8(mBuffer));
404 		break;
405 	case newPosition: {
406 		if (mState == Synchronized) {
407 			QRect rect;
408 			bool opacity;
409 			bool overlaid;
410 			QDataStream ds(mBuffer);
411 			ds >> rect;
412 			ds >> opacity;
413 			ds >> overlaid;
414 			emit connectionNewPosition(this, rect, opacity, overlaid);
415 		}
416 		break;}
417 	case newTransform: {
418 		if (mState == Synchronized) {
419 			QTransform transform;
420 			QTransform imgTransform;
421 			QPointF canvasSize;
422 			QDataStream dsTransform(mBuffer);
423 			dsTransform >> transform;
424 			dsTransform >> imgTransform;
425 			dsTransform >> canvasSize;
426 			emit connectionNewTransform(this, transform, imgTransform, canvasSize);
427 		}
428 		break;}
429 	case newFile: {
430 		if (mState == Synchronized) {
431 			qint16 op;
432 			QString filename;
433 
434 			QDataStream dsTransform(mBuffer);
435 			dsTransform >> op;
436 			dsTransform >> filename;
437 			emit connectionNewFile(this, op, filename);
438 		}
439 		break;}
440 	default:
441 		break;
442 	}
443 
444 	mCurrentDataType = Undefined;
445 	mNumBytesForCurrentDataType = 0;
446 	mBuffer.clear();
447 }
448 
synchronizedTimerTimeout()449 void DkConnection::synchronizedTimerTimeout() {
450 	mSynchronizedTimer->stop();
451 	emit connectionStopSynchronize(this);
452 }
453 
454 // DkLocalConnection --------------------------------------------------------------------
DkLocalConnection(QObject * parent)455 DkLocalConnection::DkLocalConnection(QObject* parent/* =0 */) : DkConnection(parent) {
456 }
457 
458 
processReadyRead()459 void DkLocalConnection::processReadyRead() {
460 	if (mCurrentLocalDataType == Quit) { // long message (copied from lan connection) -> does this work here correctly?
461 		readWhileBytesAvailable();
462 		return;
463 	}
464 
465 	//if (readDataIntoBuffer() <= 0)
466 	//	return;
467 	//if (!readProtocolHeader())
468 	//	return;
469 
470 	DkConnection::processReadyRead();
471 }
472 
processData()473 void DkLocalConnection::processData() {
474 
475 	if (mCurrentLocalDataType == Quit) {
476 		emit connectionQuitReceived();
477 	}
478 
479 	DkConnection::processData();
480 }
481 
readProtocolHeader()482 bool DkLocalConnection::readProtocolHeader() {
483 	QByteArray quitBA = QByteArray("QUIT").append(SeparatorToken);
484 
485 	if (mBuffer == quitBA) {
486 		mCurrentLocalDataType = Quit;
487 	} else {
488 		return DkConnection::readProtocolHeader();
489 	}
490 
491 	mBuffer.clear();
492 	mNumBytesForCurrentDataType = dataLengthForCurrentDataType();
493 	return true;
494 }
495 
496 
sendGreetingMessage(const QString & currentTitle)497 void DkLocalConnection::sendGreetingMessage(const QString& currentTitle) {
498 
499 	mCurrentTitle = currentTitle;
500 	QByteArray ba;
501 	QDataStream ds(&ba, QIODevice::ReadWrite);
502 	ds << mLocalTcpServerPort;
503 	ds << mCurrentTitle;
504 
505 	//qDebug() << "title: " << mCurrentTitle;
506 	//qDebug() << "local tcp: " << mLocalTcpServerPort;
507 	//qDebug() << "peer id: " << mPeerId;
508 
509 	QByteArray data = "GREETING";
510 	data.append(SeparatorToken);
511 	data.append(QByteArray::number(ba.size()));
512 	data.append(SeparatorToken);
513 	data.append(ba);
514 
515 	//qDebug() << "greeting message: " << data;
516 
517 	if (write(data) == data.size()) {
518 		mIsGreetingMessageSent = true;
519 	}
520 
521 }
522 
readGreetingMessage()523 void DkLocalConnection::readGreetingMessage() {
524 	QString title;
525 	QDataStream ds(mBuffer);
526 	ds >> this->mPeerServerPort;
527 	ds >> title;
528 
529 	//qDebug() << "emitting readyForUse";
530 	emit connectionReadyForUse(mPeerServerPort, title, this);
531 }
532 
sendQuitMessage()533 void DkLocalConnection::sendQuitMessage() {
534 	QByteArray ba;
535 	QDataStream ds(&ba, QIODevice::ReadWrite);
536 	ds << "updating";
537 
538 	QByteArray data = "QUIT";
539 	data.append(SeparatorToken);
540 	data.append(QByteArray::number(ba.size()));
541 	data.append(SeparatorToken);
542 	data.append(ba);
543 
544 	if (write(data) == data.size()) {
545 		mIsGreetingMessageSent = true;
546 	}
547 }
548 
549 }
550