Mercurial > trustbridge
comparison ui/downloader_win.cpp @ 23:7d07d261b003
Wrap HINTERNET handles in a class to take advantage of scoping
author | Andre Heinecke <aheinecke@intevation.de> |
---|---|
date | Thu, 20 Feb 2014 15:44:40 +0000 |
parents | fec1a610d43f |
children | 62cd56cea09b |
comparison
equal
deleted
inserted
replaced
22:bc302bbceaf5 | 23:7d07d261b003 |
---|---|
30 | 30 |
31 | 31 |
32 #define LIST_RESOURCE "/incoming/aheinecke/test" | 32 #define LIST_RESOURCE "/incoming/aheinecke/test" |
33 #define SW_RESOURCE "/incoming/aheinecke/test" | 33 #define SW_RESOURCE "/incoming/aheinecke/test" |
34 | 34 |
35 /** @brief A wrapper around a HINTERNET structure that handles closing | |
36 * | |
37 * Holds a HINTERNET structure and closes it if necessary on destruction. | |
38 * | |
39 */ | |
40 class SmartHINTERNET { | |
41 public: | |
42 SmartHINTERNET() : handle(NULL) {} | |
43 | |
44 ~SmartHINTERNET() { | |
45 if (handle) { | |
46 WinHttpCloseHandle(handle); | |
47 } | |
48 } | |
49 | |
50 HINTERNET handle; | |
51 }; | |
35 | 52 |
36 /** @brief Qt wrapper around FormatMessage | 53 /** @brief Qt wrapper around FormatMessage |
37 * | 54 * |
38 * @returns The error message of the error that occurred | 55 * @returns The error message of the error that occurred |
39 */ | 56 */ |
222 } | 239 } |
223 | 240 |
224 QDateTime Downloader::getLastModifiedHeader(HINTERNET hSession, | 241 QDateTime Downloader::getLastModifiedHeader(HINTERNET hSession, |
225 HINTERNET hConnect, LPCWSTR resource) | 242 HINTERNET hConnect, LPCWSTR resource) |
226 { | 243 { |
227 HINTERNET hRequest = NULL; | 244 SmartHINTERNET sHRequest; |
228 SYSTEMTIME lMod; | 245 SYSTEMTIME lMod; |
229 DWORD sizeOfSystemtime = sizeof (SYSTEMTIME); | 246 DWORD sizeOfSystemtime = sizeof (SYSTEMTIME); |
230 QDateTime retval; | 247 QDateTime retval; |
231 DWORD err = 0; | |
232 | 248 |
233 memset(&lMod, 0, sizeof (SYSTEMTIME)); | 249 memset(&lMod, 0, sizeof (SYSTEMTIME)); |
234 | 250 |
235 if (!hSession || !hConnect || !resource) { | 251 if (!hSession || !hConnect || !resource) { |
236 SetLastError(ERROR_INVALID_PARAMETER); | 252 SetLastError(ERROR_INVALID_PARAMETER); |
237 return retval; | 253 return retval; |
238 } | 254 } |
239 | 255 |
240 if (!createRequest(hSession, hConnect, &hRequest, L"HEAD", | 256 if (!createRequest(hSession, hConnect, &sHRequest.handle, L"HEAD", |
241 resource)) { | 257 resource)) { |
242 err = GetLastError(); | 258 return retval; |
243 goto cleanup; | 259 } |
244 } | 260 |
245 | 261 if (!WinHttpSendRequest(sHRequest.handle, |
246 if (!WinHttpSendRequest(hRequest, | |
247 WINHTTP_NO_ADDITIONAL_HEADERS, | 262 WINHTTP_NO_ADDITIONAL_HEADERS, |
248 0, WINHTTP_NO_REQUEST_DATA, 0, | 263 0, WINHTTP_NO_REQUEST_DATA, 0, |
249 0, 0)) { | 264 0, 0)) { |
250 err = GetLastError(); | 265 return retval; |
251 goto cleanup; | 266 } |
252 } | 267 |
253 | 268 |
254 | 269 if (!WinHttpReceiveResponse(sHRequest.handle, NULL)) { |
255 if (!WinHttpReceiveResponse(hRequest, NULL)) { | 270 return retval; |
256 err = GetLastError(); | 271 } |
257 goto cleanup; | 272 |
258 } | 273 if (!verifyCertificate(sHRequest.handle)) { |
259 | |
260 if (!verifyCertificate(hRequest)) { | |
261 DEBUG << "Certificate verification failed"; | 274 DEBUG << "Certificate verification failed"; |
262 goto cleanup; | 275 return retval; |
263 } | 276 } |
264 | 277 |
265 if (!(WinHttpQueryHeaders(hRequest, | 278 if (!(WinHttpQueryHeaders(sHRequest.handle, |
266 WINHTTP_QUERY_LAST_MODIFIED | | 279 WINHTTP_QUERY_LAST_MODIFIED | |
267 WINHTTP_QUERY_FLAG_SYSTEMTIME, | 280 WINHTTP_QUERY_FLAG_SYSTEMTIME, |
268 NULL, | 281 NULL, |
269 &lMod, | 282 &lMod, |
270 &sizeOfSystemtime, | 283 &sizeOfSystemtime, |
271 WINHTTP_NO_HEADER_INDEX))) { | 284 WINHTTP_NO_HEADER_INDEX))) { |
272 err = GetLastError(); | 285 return retval; |
273 goto cleanup; | |
274 } | 286 } |
275 | 287 |
276 retval = QDateTime(QDate(lMod.wYear, lMod.wMonth, lMod.wDay), | 288 retval = QDateTime(QDate(lMod.wYear, lMod.wMonth, lMod.wDay), |
277 QTime(lMod.wHour, lMod.wMinute, lMod.wSecond, | 289 QTime(lMod.wHour, lMod.wMinute, lMod.wSecond, |
278 lMod.wMilliseconds), | 290 lMod.wMilliseconds), |
279 Qt::UTC); | 291 Qt::UTC); |
280 | |
281 // TODO remove and replace with a smart handle | |
282 cleanup: | |
283 if (hRequest) { | |
284 WinHttpCloseHandle(hRequest); | |
285 } | |
286 | |
287 // Close handle might overwrite the last error. | |
288 SetLastError(err); | |
289 return retval; | 292 return retval; |
290 } | 293 } |
291 | 294 |
292 bool Downloader::downloadFile(HINTERNET hSession, HINTERNET hConnect, | 295 bool Downloader::downloadFile(HINTERNET hSession, HINTERNET hConnect, |
293 LPCWSTR resource, const QString &fileName, DWORD maxSize) | 296 LPCWSTR resource, const QString &fileName, DWORD maxSize) |
294 { | 297 { |
295 HINTERNET hRequest = NULL; | 298 SmartHINTERNET sHRequest; |
296 bool retval = false; | 299 bool retval = false; |
297 DWORD bytesAvailable = 0, | 300 DWORD bytesAvailable = 0, |
298 err = 0, | |
299 bytesRead = 0, | 301 bytesRead = 0, |
300 totalDownloaded = 0, | 302 totalDownloaded = 0, |
301 contentLength = 0, | 303 contentLength = 0, |
302 sizeOfDWORD = sizeof (DWORD); | 304 sizeOfDWORD = sizeof (DWORD); |
303 | 305 |
306 if (!hSession || !hConnect || !resource) { | 308 if (!hSession || !hConnect || !resource) { |
307 SetLastError(ERROR_INVALID_PARAMETER); | 309 SetLastError(ERROR_INVALID_PARAMETER); |
308 return retval; | 310 return retval; |
309 } | 311 } |
310 | 312 |
311 if (!createRequest(hSession, hConnect, &hRequest, L"GET", | 313 if (!createRequest(hSession, hConnect, &sHRequest.handle, L"GET", |
312 resource)) { | 314 resource)) { |
313 err = GetLastError(); | 315 return retval; |
314 goto cleanup; | 316 } |
315 } | 317 |
316 | 318 if (!WinHttpSendRequest(sHRequest.handle, |
317 if (!WinHttpSendRequest(hRequest, | |
318 WINHTTP_NO_ADDITIONAL_HEADERS, | 319 WINHTTP_NO_ADDITIONAL_HEADERS, |
319 0, WINHTTP_NO_REQUEST_DATA, 0, | 320 0, WINHTTP_NO_REQUEST_DATA, 0, |
320 0, 0)) { | 321 0, 0)) { |
321 err = GetLastError(); | 322 return retval; |
322 goto cleanup; | 323 } |
323 } | 324 |
324 | 325 |
325 | 326 if (!WinHttpReceiveResponse(sHRequest.handle, NULL)) { |
326 if (!WinHttpReceiveResponse(hRequest, NULL)) { | 327 return retval; |
327 err = GetLastError(); | 328 } |
328 goto cleanup; | 329 |
329 } | 330 if (!verifyCertificate(sHRequest.handle)) { |
330 | |
331 if (!verifyCertificate(hRequest)) { | |
332 DEBUG << "Certificate verification failed"; | 331 DEBUG << "Certificate verification failed"; |
333 goto cleanup; | 332 return retval; |
334 } | 333 } |
335 | 334 |
336 | 335 |
337 if (!(WinHttpQueryHeaders(hRequest, | 336 if (!(WinHttpQueryHeaders(sHRequest.handle, |
338 WINHTTP_QUERY_CONTENT_LENGTH | | 337 WINHTTP_QUERY_CONTENT_LENGTH | |
339 WINHTTP_QUERY_FLAG_NUMBER, | 338 WINHTTP_QUERY_FLAG_NUMBER, |
340 NULL, | 339 NULL, |
341 &contentLength, | 340 &contentLength, |
342 &sizeOfDWORD, | 341 &sizeOfDWORD, |
345 // much we download. | 344 // much we download. |
346 DEBUG << "No content-length"; | 345 DEBUG << "No content-length"; |
347 } | 346 } |
348 | 347 |
349 if (contentLength > maxSize) { | 348 if (contentLength > maxSize) { |
350 err = ERROR_INVALID_DATA; | 349 return retval; |
351 goto cleanup; | |
352 } | 350 } |
353 | 351 |
354 if (contentLength) { | 352 if (contentLength) { |
355 QFileInfo finf(fileName); | 353 QFileInfo finf(fileName); |
356 if (finf.exists() && finf.isReadable() && | 354 if (finf.exists() && finf.isReadable() && |
357 finf.size() == contentLength) { | 355 finf.size() == contentLength) { |
358 // We already have data of the same size | 356 // We already have data of the same size |
359 // No need to waste bandwidth. | 357 // No need to waste bandwidth. |
360 DEBUG << "Skipping download because file exists"; | 358 DEBUG << "Skipping download because file exists"; |
361 retval = true; | 359 retval = true; |
362 goto cleanup; | 360 return retval; |
363 } | 361 } |
364 } | 362 } |
365 | 363 |
366 // Open / Create the file to write to. | 364 // Open / Create the file to write to. |
367 if (!outputFile.open(QIODevice::WriteOnly)) { | 365 if (!outputFile.open(QIODevice::WriteOnly)) { |
368 DEBUG << "Failed to open file"; | 366 DEBUG << "Failed to open file"; |
369 err = GetLastError(); | 367 return retval; |
370 goto cleanup; | |
371 } | 368 } |
372 | 369 |
373 DEBUG << "output file size: " << outputFile.size(); | 370 DEBUG << "output file size: " << outputFile.size(); |
374 do | 371 do |
375 { | 372 { |
376 char outBuf[8192]; // 8KB is the internal buffer size of winhttp | 373 char outBuf[8192]; // 8KB is the internal buffer size of winhttp |
377 memset(outBuf, 0, sizeof(outBuf)); | 374 memset(outBuf, 0, sizeof(outBuf)); |
378 bytesRead = 0; | 375 bytesRead = 0; |
379 | 376 |
380 if (!WinHttpQueryDataAvailable(hRequest, &bytesAvailable)) { | 377 if (!WinHttpQueryDataAvailable(sHRequest.handle, &bytesAvailable)) { |
381 DEBUG << "Querying for available data failed"; | 378 DEBUG << "Querying for available data failed"; |
382 retval = false; | 379 retval = false; |
383 err = GetLastError(); | |
384 break; | 380 break; |
385 } | 381 } |
386 | 382 |
387 if (!bytesAvailable) { | 383 if (!bytesAvailable) { |
388 // Might indicate that we are done. | 384 // Might indicate that we are done. |
390 } | 386 } |
391 | 387 |
392 if (bytesAvailable > maxSize) { | 388 if (bytesAvailable > maxSize) { |
393 DEBUG << "File to large"; | 389 DEBUG << "File to large"; |
394 retval = false; | 390 retval = false; |
395 err = ERROR_INVALID_DATA; | 391 SetLastError(ERROR_INVALID_DATA); |
396 break; | 392 break; |
397 } | 393 } |
398 | 394 |
399 if (!WinHttpReadData(hRequest, (LPVOID)outBuf, | 395 if (!WinHttpReadData(sHRequest.handle, (LPVOID)outBuf, |
400 sizeof(outBuf), &bytesRead)) { | 396 sizeof(outBuf), &bytesRead)) { |
401 DEBUG << "Error reading data"; | 397 DEBUG << "Error reading data"; |
402 err = GetLastError(); | |
403 break; | 398 break; |
404 } else { | 399 } else { |
405 if (bytesRead) { | 400 if (bytesRead) { |
406 // Write data to file. | 401 // Write data to file. |
407 if (outputFile.write(outBuf, bytesRead) != | 402 if (outputFile.write(outBuf, bytesRead) != |
408 bytesRead) { | 403 bytesRead) { |
409 err = GetLastError(); | |
410 DEBUG << "Error writing to file."; | 404 DEBUG << "Error writing to file."; |
411 retval = false; | 405 retval = false; |
412 } | 406 } |
413 // Completed a read / write cycle. If not error follows | 407 // Completed a read / write cycle. If not error follows |
414 // the download was successful. | 408 // the download was successful. |
429 retval = false; | 423 retval = false; |
430 break; | 424 break; |
431 } | 425 } |
432 } while (bytesAvailable > 0); | 426 } while (bytesAvailable > 0); |
433 | 427 |
434 // TODO remove and replace with a smart handle | |
435 cleanup: | |
436 | |
437 if (retval && outputFile.isOpen()) { | 428 if (retval && outputFile.isOpen()) { |
438 // Actually save the file to disk / move to homedir | 429 // Actually save the file to disk / move to homedir |
439 retval = outputFile.commit(); | 430 retval = outputFile.commit(); |
440 } | 431 } |
441 | 432 |
442 if (hRequest) { | |
443 WinHttpCloseHandle(hRequest); | |
444 } | |
445 | |
446 // Close handle might overwrite the last error. | |
447 SetLastError(err); | |
448 return retval; | 433 return retval; |
449 } | 434 } |
450 | 435 |
451 void Downloader::run() { | 436 void Downloader::run() { |
452 bool results = false; | 437 bool results = false; |
453 HINTERNET hSession = NULL, | 438 SmartHINTERNET sHSession; |
454 hConnect = NULL; | 439 SmartHINTERNET sHConnect; |
455 wchar_t wUrl[mUrl.size() + 1]; | 440 wchar_t wUrl[mUrl.size() + 1]; |
456 QDateTime lastModifiedSoftware; | 441 QDateTime lastModifiedSoftware; |
457 QDateTime lastModifiedList; | 442 QDateTime lastModifiedList; |
458 | 443 |
459 int rc = 0; | 444 int rc = 0; |
469 | 454 |
470 // Should not be necessary because we initialized the memory | 455 // Should not be necessary because we initialized the memory |
471 wUrl[rc] = '\0'; | 456 wUrl[rc] = '\0'; |
472 | 457 |
473 // Initialize connection | 458 // Initialize connection |
474 if (!openSession(&hSession)) { | 459 if (!openSession(&sHConnect.handle)) { |
475 DEBUG << "Failed to open session: " << getLastErrorMsg(); | 460 DEBUG << "Failed to open session: " << getLastErrorMsg(); |
476 return; | 461 return; |
477 } | 462 } |
478 if (!initializeConnection(hSession, &hConnect, wUrl)) { | 463 if (!initializeConnection(sHConnect.handle, &sHConnect.handle, wUrl)) { |
479 DEBUG << "Failed to initialize connection: " << getLastErrorMsg(); | 464 DEBUG << "Failed to initialize connection: " << getLastErrorMsg(); |
480 goto cleanup; | 465 return; |
481 } | 466 } |
482 | 467 |
483 | 468 |
484 lastModifiedSoftware = getLastModifiedHeader(hSession, hConnect, | 469 lastModifiedSoftware = getLastModifiedHeader(sHConnect.handle, sHConnect.handle, |
485 L""SW_RESOURCE); | 470 L""SW_RESOURCE); |
486 | 471 |
487 lastModifiedList = getLastModifiedHeader(hSession, hConnect, | 472 lastModifiedList = getLastModifiedHeader(sHConnect.handle, sHConnect.handle, |
488 L""LIST_RESOURCE); | 473 L""LIST_RESOURCE); |
489 | 474 |
490 if (!lastModifiedList.isValid() || !lastModifiedSoftware.isValid()) { | 475 if (!lastModifiedList.isValid() || !lastModifiedSoftware.isValid()) { |
491 DEBUG << "Could not read headers: " << getLastErrorMsg(); | 476 DEBUG << "Could not read headers: " << getLastErrorMsg(); |
492 goto cleanup; | 477 return; |
493 } | 478 } |
494 | 479 |
495 if (!mLastModSW.isValid() || lastModifiedSoftware > mLastModSW) { | 480 if (!mLastModSW.isValid() || lastModifiedSoftware > mLastModSW) { |
496 QString dataDirectory = getDataDirectory(); | 481 QString dataDirectory = getDataDirectory(); |
497 | 482 |
498 if (dataDirectory.isEmpty()) { | 483 if (dataDirectory.isEmpty()) { |
499 DEBUG << "Failed to get data directory"; | 484 DEBUG << "Failed to get data directory"; |
500 goto cleanup; | 485 return; |
501 } | 486 } |
502 | 487 |
503 QString fileName = dataDirectory.append("/SW-") | 488 QString fileName = dataDirectory.append("/SW-") |
504 .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) | 489 .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) |
505 .append(".exe"); | 490 .append(".exe"); |
506 | 491 |
507 DEBUG << "fileName: " << fileName; | 492 DEBUG << "fileName: " << fileName; |
508 | 493 |
509 if (!downloadFile(hSession, hConnect, L""SW_RESOURCE, | 494 if (!downloadFile(sHConnect.handle, sHConnect.handle, L""SW_RESOURCE, |
510 fileName, MAX_SW_SIZE)) { | 495 fileName, MAX_SW_SIZE)) { |
511 DEBUG << "Error downloading File: " << getLastErrorMsg(); | 496 DEBUG << "Error downloading File: " << getLastErrorMsg(); |
512 goto cleanup; | 497 return; |
513 } | 498 } |
514 | 499 |
515 emit newSoftwareAvailable(fileName, lastModifiedSoftware); | 500 emit newSoftwareAvailable(fileName, lastModifiedSoftware); |
516 } else if (!mLastModList.isValid() || lastModifiedList > mLastModList) { | 501 } else if (!mLastModList.isValid() || lastModifiedList > mLastModList) { |
517 QString dataDirectory = getDataDirectory(); | 502 QString dataDirectory = getDataDirectory(); |
518 | 503 |
519 if (dataDirectory.isEmpty()) { | 504 if (dataDirectory.isEmpty()) { |
520 DEBUG << "Failed to get data directory"; | 505 DEBUG << "Failed to get data directory"; |
521 goto cleanup; | 506 return; |
522 } | 507 } |
523 | 508 |
524 QString fileName = dataDirectory.append("/list-") | 509 QString fileName = dataDirectory.append("/list-") |
525 .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) | 510 .append(lastModifiedSoftware.toString("yyyymmddHHmmss")) |
526 .append(".txt"); | 511 .append(".txt"); |
527 | 512 |
528 DEBUG << "fileName: " << fileName; | 513 DEBUG << "fileName: " << fileName; |
529 | 514 |
530 if (!downloadFile(hSession, hConnect, L""LIST_RESOURCE, | 515 if (!downloadFile(sHConnect.handle, sHConnect.handle, L""LIST_RESOURCE, |
531 fileName, MAX_LIST_SIZE)) { | 516 fileName, MAX_LIST_SIZE)) { |
532 DEBUG << "Error downloading File: " << getLastErrorMsg(); | 517 DEBUG << "Error downloading File: " << getLastErrorMsg(); |
533 goto cleanup; | 518 return; |
534 } | 519 } |
535 | 520 |
536 emit newListAvailable(fileName, lastModifiedList); | 521 emit newListAvailable(fileName, lastModifiedList); |
537 } | 522 } |
538 | 523 |
539 DEBUG << "SW date: " << lastModifiedSoftware; | 524 DEBUG << "SW date: " << lastModifiedSoftware; |
540 DEBUG << "List date: " << lastModifiedList; | 525 DEBUG << "List date: " << lastModifiedList; |
541 | |
542 /*if (!WinHttpQueryDataAvailable(hRequest, &dataAvaiable)) { | |
543 DEBUG << "Failed to query data Available: " << getLastErrorMsg(); | |
544 goto cleanup; | |
545 }*/ | |
546 | 526 |
547 if (!results) { | 527 if (!results) { |
548 // Report any errors. | 528 // Report any errors. |
549 DEBUG << "Error" << GetLastError(); | 529 DEBUG << "Error" << GetLastError(); |
550 emit error(tr("Unknown Problem when connecting"), ErrUnknown); | 530 emit error(tr("Unknown Problem when connecting"), ErrUnknown); |
551 } | 531 } |
552 | 532 |
553 // TODO remove and replace with a smart handle | |
554 cleanup: | |
555 if (hConnect) { | |
556 WinHttpCloseHandle(hConnect); | |
557 } | |
558 | |
559 if (hSession) { | |
560 WinHttpCloseHandle(hSession); | |
561 } | |
562 return; | 533 return; |
563 } | 534 } |
564 #endif | 535 #endif |