|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the tools applications of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 #include "qtcesterconnection.h" |
|
42 #include <transfer_global.h> |
|
43 #include <QtCore/QDir> |
|
44 #include <QtCore/QFile> |
|
45 #include <QtCore/QFileInfo> |
|
46 #include <QtNetwork/QTcpSocket> |
|
47 #include <QtNetwork/QHostAddress> |
|
48 |
|
49 extern void debugOutput(const QString& text, int level); |
|
50 |
|
51 #pragma warning(disable:4996) |
|
52 |
|
53 #define END_ERROR(s, a) \ |
|
54 if(a) qDebug() << a; \ |
|
55 _freeSocket(s); \ |
|
56 return false; |
|
57 |
|
58 QtCesterConnection::QtCesterConnection() |
|
59 : AbstractRemoteConnection() |
|
60 { |
|
61 } |
|
62 |
|
63 QtCesterConnection::~QtCesterConnection() |
|
64 { |
|
65 } |
|
66 |
|
67 bool QtCesterConnection::connect(QVariantList&) |
|
68 { |
|
69 // We connect with each command, so this is always true |
|
70 // The command itself will fail then |
|
71 connected = true; |
|
72 return true; |
|
73 } |
|
74 |
|
75 void QtCesterConnection::disconnect() |
|
76 { |
|
77 connected = false; |
|
78 } |
|
79 |
|
80 bool QtCesterConnection::isConnected() const |
|
81 { |
|
82 return connected; |
|
83 } |
|
84 |
|
85 bool QtCesterConnection::copyFileToDevice(const QString &localSource, const QString &deviceDest, bool failIfExists) |
|
86 { |
|
87 debugOutput( qPrintable(QString::fromLatin1("Copy File: %1 -> %2").arg(localSource).arg(deviceDest)),0); |
|
88 QFile localFile(localSource); |
|
89 QFileInfo info(localSource); |
|
90 if (!localFile.exists() || !localFile.open(QIODevice::ReadOnly)) { |
|
91 qDebug() << "Could not open File!"; |
|
92 return false; |
|
93 } |
|
94 |
|
95 QTcpSocket* socket = 0; |
|
96 if (!_initCommand(socket, COMMAND_CREATE_FILE)) { |
|
97 END_ERROR(socket, "Could not initialized command"); |
|
98 } |
|
99 |
|
100 CreateFileOptions option; |
|
101 strcpy(option.fileName, qPrintable(deviceDest)); |
|
102 #ifdef Q_OS_WIN |
|
103 // Copy FileTime for update verification |
|
104 FILETIME creationTime, accessTime, writeTime; |
|
105 HANDLE localHandle = CreateFile(localSource.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); |
|
106 if (localHandle != INVALID_HANDLE_VALUE) { |
|
107 if (GetFileTime(localHandle, &creationTime, &accessTime, &writeTime)) { |
|
108 LocalFileTimeToFileTime(&writeTime, &writeTime); |
|
109 option.fileTime = writeTime; |
|
110 } |
|
111 CloseHandle(localHandle); |
|
112 } |
|
113 DWORD attributes = GetFileAttributes(localSource.utf16()); |
|
114 if (attributes != -1 ) |
|
115 option.fileAttributes = attributes; |
|
116 #endif |
|
117 option.fileSize = info.size(); |
|
118 option.overwriteExisting = !failIfExists; |
|
119 |
|
120 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
121 END_ERROR(socket, "Could not send options..."); |
|
122 } |
|
123 |
|
124 if (!_checkResult(socket)) { |
|
125 END_ERROR(socket, "Server did not accept configuration"); |
|
126 } |
|
127 |
|
128 int bytesWritten = 0; |
|
129 const int bufferSize = 1024; |
|
130 QByteArray data; |
|
131 while (bytesWritten < option.fileSize) { |
|
132 data = localFile.read(bufferSize); |
|
133 bytesWritten += data.size(); |
|
134 #ifdef Q_OS_WIN |
|
135 wprintf( L"%s -> %s (%d / %d) %d %%\r", localSource.utf16() , deviceDest.utf16(), |
|
136 bytesWritten , option.fileSize, (100*bytesWritten)/option.fileSize ); |
|
137 #endif |
|
138 if (!_sendData(socket, data.constData(), data.size())) { |
|
139 END_ERROR(socket, "Error during file transfer"); |
|
140 } |
|
141 if (!_checkResult(socket)) { |
|
142 END_ERROR(socket, "Got some strange result"); |
|
143 } |
|
144 } |
|
145 #ifdef Q_OS_WIN |
|
146 wprintf( L"\n"); // We should jump to next line... |
|
147 #endif |
|
148 if (bytesWritten != option.fileSize) { |
|
149 END_ERROR(socket, "Did not send sufficient data"); |
|
150 } |
|
151 _freeSocket(socket); |
|
152 return true; |
|
153 } |
|
154 |
|
155 bool QtCesterConnection::copyDirectoryToDevice(const QString &localSource, const QString &deviceDest, bool recursive) |
|
156 { |
|
157 QTcpSocket* socket = NULL; |
|
158 QFileInfo info(localSource); |
|
159 if (!info.exists() || !info.isDir()) { |
|
160 END_ERROR(socket, "Input directory invalid"); |
|
161 } |
|
162 |
|
163 createDirectory(deviceDest, true); |
|
164 QDir dir(localSource); |
|
165 QFileInfoList list = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); |
|
166 foreach(QFileInfo item, list) { |
|
167 QString targetName = deviceDest + QLatin1String("\\") + item.fileName(); |
|
168 if (item.isDir()) { |
|
169 if (recursive) { |
|
170 if (!copyDirectoryToDevice(item.absoluteFilePath() , targetName, recursive)) |
|
171 return false; |
|
172 } |
|
173 } else { |
|
174 if (!copyFileToDevice(item.absoluteFilePath(), targetName)) |
|
175 return false; |
|
176 } |
|
177 } |
|
178 return true; |
|
179 } |
|
180 |
|
181 bool QtCesterConnection::copyFileFromDevice(const QString &deviceSource, const QString &localDest, bool failIfExists) |
|
182 { |
|
183 QFile targetFile(localDest); |
|
184 QTcpSocket* socket = 0; |
|
185 if (targetFile.exists() && failIfExists) { |
|
186 END_ERROR(socket, "Local file not supposed to be overwritten"); |
|
187 } |
|
188 |
|
189 if (!targetFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { |
|
190 END_ERROR(socket, "Could not open local file for writing"); |
|
191 } |
|
192 |
|
193 if (!_initCommand(socket, COMMAND_READ_FILE)) { |
|
194 END_ERROR(socket, "Could not establish connection"); |
|
195 } |
|
196 |
|
197 ReadFileOptions option; |
|
198 strcpy(option.fileName, qPrintable(deviceSource)); |
|
199 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
200 END_ERROR(socket, "Could not send options"); |
|
201 } |
|
202 |
|
203 QByteArray data; |
|
204 if (!_receiveData(socket, data)) { |
|
205 END_ERROR(socket, "Did not receive any data"); |
|
206 } |
|
207 |
|
208 ReadFileReply* reply = (ReadFileReply*) data.data(); |
|
209 if (!reply->fileValid) { |
|
210 END_ERROR(socket, "Requested file invalid"); |
|
211 } |
|
212 |
|
213 int fileSize = reply->fileSize; |
|
214 int currentSize = 0; |
|
215 // ### TODO: make a little bit more error-prone |
|
216 do { |
|
217 _sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS)); |
|
218 _receiveData(socket, data); |
|
219 currentSize += data.size(); |
|
220 targetFile.write(data); |
|
221 } while(currentSize < fileSize); |
|
222 |
|
223 _freeSocket(socket); |
|
224 targetFile.close(); |
|
225 return true; |
|
226 } |
|
227 |
|
228 bool QtCesterConnection::copyDirectoryFromDevice(const QString& /*deviceSource*/ |
|
229 , const QString& /*localDest*/ |
|
230 , bool /*recursive*/) |
|
231 { |
|
232 qDebug() << "To be implemented!! Should not be needed for autotest system"; |
|
233 exit(-1); |
|
234 return false; |
|
235 } |
|
236 |
|
237 bool QtCesterConnection::copyFile(const QString &srcFile, const QString &destFile, bool failIfExists) |
|
238 { |
|
239 QTcpSocket* socket = 0; |
|
240 if (!_initCommand(socket, COMMAND_COPY_FILE)) { |
|
241 END_ERROR(socket, "Could not establish connection for copy"); |
|
242 } |
|
243 |
|
244 CopyFileOptions option; |
|
245 strcpy(option.from, qPrintable(srcFile)); |
|
246 strcpy(option.to, qPrintable(destFile)); |
|
247 option.overwriteExisting = !failIfExists; |
|
248 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
249 END_ERROR(socket, "Could not send copy options"); |
|
250 } |
|
251 |
|
252 if (!_checkResult(socket)) { |
|
253 END_ERROR(socket, "Copy failed"); |
|
254 } |
|
255 |
|
256 _freeSocket(socket); |
|
257 return true; |
|
258 } |
|
259 |
|
260 bool QtCesterConnection::copyDirectory(const QString &srcDirectory, const QString &destDirectory, |
|
261 bool recursive) |
|
262 { |
|
263 QTcpSocket* socket = 0; |
|
264 if (!_initCommand(socket, COMMAND_COPY_DIRECTORY)) { |
|
265 END_ERROR(socket, "Could not establish connection for dir copy"); |
|
266 } |
|
267 |
|
268 CopyDirectoryOptions option; |
|
269 strcpy(option.from, qPrintable(srcDirectory)); |
|
270 strcpy(option.to, qPrintable(destDirectory)); |
|
271 option.recursive = recursive; |
|
272 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
273 END_ERROR(socket, "Could not send dir copy options"); |
|
274 } |
|
275 |
|
276 if (!_checkResult(socket)) { |
|
277 END_ERROR(socket, "Dir Copy failed"); |
|
278 } |
|
279 |
|
280 _freeSocket(socket); |
|
281 return true; |
|
282 } |
|
283 |
|
284 bool QtCesterConnection::deleteFile(const QString &fileName) |
|
285 { |
|
286 QTcpSocket* socket = 0; |
|
287 if (!_initCommand(socket, COMMAND_DELETE_FILE)) { |
|
288 END_ERROR(socket, "Could not establish connection for file deletion"); |
|
289 } |
|
290 |
|
291 DeleteFileOptions option; |
|
292 strcpy(option.fileName, qPrintable(fileName)); |
|
293 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
294 END_ERROR(socket, "Could not send file options"); |
|
295 } |
|
296 |
|
297 if (!_checkResult(socket)) { |
|
298 //END_ERROR(socket, "File Deletion failed"); |
|
299 // This is actually not an error so ignore it. |
|
300 } |
|
301 |
|
302 _freeSocket(socket); |
|
303 return true; |
|
304 } |
|
305 |
|
306 bool QtCesterConnection::deleteDirectory(const QString &directory, bool recursive, bool failIfContentExists) |
|
307 { |
|
308 QTcpSocket* socket = 0; |
|
309 if (!_initCommand(socket, COMMAND_DELETE_DIRECTORY)) { |
|
310 END_ERROR(socket, "Could not establish connection for dir deletion"); |
|
311 } |
|
312 |
|
313 DeleteDirectoryOptions option; |
|
314 strcpy(option.dirName, qPrintable(directory)); |
|
315 option.recursive = recursive; |
|
316 option.failIfContentExists = failIfContentExists; |
|
317 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
318 END_ERROR(socket, "Could not send dir options"); |
|
319 } |
|
320 |
|
321 if (!_checkResult(socket)) { |
|
322 // we do not write an error as this will fail a lot on recursive. |
|
323 END_ERROR(socket, 0); |
|
324 } |
|
325 |
|
326 _freeSocket(socket); |
|
327 return true; |
|
328 } |
|
329 |
|
330 bool QtCesterConnection::execute(QString program, |
|
331 QString arguments, |
|
332 int timeout, |
|
333 int *returnValue) |
|
334 { |
|
335 QTcpSocket* socket = 0; |
|
336 if (!_initCommand(socket, COMMAND_EXECUTE)) { |
|
337 END_ERROR(socket, "Could not establish connection for dir deletion"); |
|
338 } |
|
339 |
|
340 ExecuteOptions options; |
|
341 strcpy(options.appName, qPrintable(program)); |
|
342 QStringList argList = arguments.split(QLatin1Char(' ')); |
|
343 options.argumentsCount = qMin(argList.size(), MAX_ARGUMENTS); |
|
344 options.waitForFinished = true; |
|
345 options.timeout = timeout; |
|
346 if (!_sendData(socket, (char*) &options, sizeof(options))) { |
|
347 END_ERROR(socket, "Could not send dir options"); |
|
348 } |
|
349 if (!_checkResult(socket)) { |
|
350 END_ERROR(socket, "Did not receive an answer"); |
|
351 } |
|
352 |
|
353 for (int i=0; i < options.argumentsCount; ++i) { |
|
354 char someData[MAX_NAME_LENGTH]; |
|
355 strcpy(someData, qPrintable(argList[i])); |
|
356 if (!_sendData(socket, someData, MAX_NAME_LENGTH)) { |
|
357 END_ERROR(socket, "Could not send argument"); |
|
358 } |
|
359 if (!_checkResult(socket)) { |
|
360 END_ERROR(socket, "Failure in argument send"); |
|
361 } |
|
362 } |
|
363 |
|
364 // trigger the startup |
|
365 if (!_sendData(socket, COMMAND_SUCCESS, strlen(COMMAND_SUCCESS))) { |
|
366 END_ERROR(socket, "Could not trigger startup"); |
|
367 } |
|
368 |
|
369 const int waitTime = 60 * 60 * 1000; |
|
370 if (!socket->waitForReadyRead(waitTime)) { |
|
371 END_ERROR(socket, "Process timed out"); |
|
372 } |
|
373 |
|
374 QByteArray result = socket->readAll(); |
|
375 if (result != COMMAND_SUCCESS) { |
|
376 if (returnValue) |
|
377 *returnValue = -1; // just some at least |
|
378 END_ERROR(socket, "Application did not start or returned error"); |
|
379 } |
|
380 |
|
381 if (returnValue) |
|
382 *returnValue = 0; |
|
383 _freeSocket(socket); |
|
384 return true; |
|
385 } |
|
386 |
|
387 bool QtCesterConnection::createDirectory(const QString &path, bool deleteBefore) |
|
388 { |
|
389 if (deleteBefore) |
|
390 deleteDirectory(path, true, true); |
|
391 |
|
392 QTcpSocket* socket = 0; |
|
393 if (!_initCommand(socket, COMMAND_CREATE_DIRECTORY)) { |
|
394 END_ERROR(socket, "Could not establish connection for dir creation"); |
|
395 } |
|
396 |
|
397 CreateDirectoryOptions option; |
|
398 strcpy(option.dirName, qPrintable(path)); |
|
399 option.recursively = true; |
|
400 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
401 END_ERROR(socket, "Could not send dir options"); |
|
402 } |
|
403 |
|
404 if (!_checkResult(socket)) { |
|
405 END_ERROR(socket, "Dir creation failed"); |
|
406 } |
|
407 |
|
408 _freeSocket(socket); |
|
409 return true; |
|
410 } |
|
411 |
|
412 bool QtCesterConnection::timeStampForLocalFileTime(FILETIME* fTime) const |
|
413 { |
|
414 if (!fTime) |
|
415 return false; |
|
416 |
|
417 FILETIME copyTime = *fTime; |
|
418 LocalFileTimeToFileTime(©Time, ©Time); |
|
419 |
|
420 QTcpSocket* socket = 0; |
|
421 if (!_initCommand(socket, COMMAND_TIME_STAMP)) { |
|
422 END_ERROR(socket, "Could not establish time stamp connection"); |
|
423 } |
|
424 |
|
425 if (!_sendData(socket, (char*) ©Time, sizeof(copyTime))) { |
|
426 END_ERROR(socket, "Could not send stamp time"); |
|
427 } |
|
428 |
|
429 QByteArray data; |
|
430 if (!_receiveData(socket, data)) { |
|
431 END_ERROR(socket, "Did not receive time stamp or connection interrupted"); |
|
432 } |
|
433 |
|
434 copyTime = *((FILETIME*)data.data()); |
|
435 if (copyTime.dwLowDateTime == -1 && copyTime.dwHighDateTime == -1) { |
|
436 END_ERROR(socket, "remote Time stamp failed!"); |
|
437 } |
|
438 |
|
439 *fTime = copyTime; |
|
440 _freeSocket(socket); |
|
441 return true; |
|
442 } |
|
443 |
|
444 bool QtCesterConnection::fileCreationTime(const QString &fileName, FILETIME* deviceCreationTime) const |
|
445 { |
|
446 if (!deviceCreationTime) |
|
447 return false; |
|
448 |
|
449 QTcpSocket* socket = 0; |
|
450 if (!_initCommand(socket, COMMAND_FILE_TIME)) { |
|
451 END_ERROR(socket, "Could not establish connection for file time access"); |
|
452 } |
|
453 |
|
454 FileTimeOptions option; |
|
455 strcpy(option.fileName, qPrintable(fileName)); |
|
456 if (!_sendData(socket, (char*) &option, sizeof(option))) { |
|
457 END_ERROR(socket, "Could not send file time name"); |
|
458 } |
|
459 |
|
460 QByteArray data; |
|
461 if (!_receiveData(socket, data)) { |
|
462 END_ERROR(socket, "File Time request failed"); |
|
463 } |
|
464 |
|
465 FILETIME* resultTime = (FILETIME*) data.data(); |
|
466 if (resultTime->dwLowDateTime == -1 && resultTime->dwHighDateTime == -1) { |
|
467 END_ERROR(socket, 0); |
|
468 debugOutput("Could not access file time", 0); |
|
469 } |
|
470 |
|
471 *deviceCreationTime = *resultTime; |
|
472 _freeSocket(socket); |
|
473 return true; |
|
474 } |
|
475 |
|
476 bool QtCesterConnection::_createSocket(QTcpSocket*& result) const |
|
477 { |
|
478 QTcpSocket* sock = new QTcpSocket(); |
|
479 QByteArray ipAddress = qgetenv("DEVICE_IP"); |
|
480 if (ipAddress.isEmpty()) { |
|
481 qWarning("Error: You need to have DEVICE_IP set"); |
|
482 exit(0); |
|
483 } |
|
484 sock->connectToHost(QHostAddress(QString(ipAddress)), 12145); |
|
485 |
|
486 if (!sock->waitForConnected()) { |
|
487 qDebug() << "connection timeout..."; |
|
488 result = NULL; |
|
489 return false; |
|
490 } |
|
491 result = sock; |
|
492 return true; |
|
493 } |
|
494 |
|
495 void QtCesterConnection::_freeSocket(QTcpSocket*& sock) const |
|
496 { |
|
497 if (!sock) |
|
498 return; |
|
499 if (sock->state() == QAbstractSocket::ConnectedState) { |
|
500 sock->disconnectFromHost(); |
|
501 // seems like no need to wait |
|
502 //sock->waitForDisconnected(); |
|
503 } |
|
504 delete sock; |
|
505 sock = NULL; |
|
506 #ifdef Q_OS_WIN |
|
507 Sleep(100); |
|
508 #endif |
|
509 } |
|
510 |
|
511 bool QtCesterConnection::_initCommand(QTcpSocket*& sock, const char* command) const |
|
512 { |
|
513 QTcpSocket* socket = NULL; |
|
514 if (!_createSocket(socket)) { |
|
515 END_ERROR(socket, "Could not connect to server"); |
|
516 } |
|
517 |
|
518 if (!_sendData(socket, command, strlen(command)) || |
|
519 !_checkResult(socket)) { |
|
520 END_ERROR(socket, "Cound not send command"); |
|
521 } |
|
522 sock = socket; |
|
523 return true; |
|
524 } |
|
525 |
|
526 bool QtCesterConnection::_sendData(QTcpSocket*& sock, const char* data, int dataSize) const |
|
527 { |
|
528 int amount = sock->write(data, dataSize); |
|
529 if (amount != dataSize) { |
|
530 fprintf(stderr, "*******COULD NOT SEND ENOUGH DATA*************\n"); |
|
531 } |
|
532 return sock->waitForBytesWritten(); |
|
533 } |
|
534 |
|
535 bool QtCesterConnection::_receiveData(QTcpSocket*& sock, QByteArray& data) const |
|
536 { |
|
537 if (!sock->waitForReadyRead()) { |
|
538 qDebug() << "did not receive any data"; |
|
539 return false; |
|
540 } |
|
541 data = sock->readAll(); |
|
542 return true; |
|
543 } |
|
544 |
|
545 bool QtCesterConnection::_checkResult(QTcpSocket*& sock) const |
|
546 { |
|
547 QByteArray response; |
|
548 if (!_receiveData(sock, response) || response != COMMAND_SUCCESS) |
|
549 return false; |
|
550 return true; |
|
551 } |
|
552 |