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