1 /*
   2  * Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #if _MSC_VER > 1000
  27 #pragma once
  28 #endif // _MSC_VER > 1000
  29 
  30 #define STRICT
  31 #ifndef _WIN32_WINNT
  32 /* REMIND : 0x500 means Windows 2000 .. seems like we can update
  33  * for Windows XP when we move the SDK and build platform
  34  */
  35 #define _WIN32_WINNT 0x0500
  36 #endif
  37 #define _ATL_APARTMENT_THREADED
  38 
  39 
  40 #include <atlbase.h>
  41 //You may derive a class from CComModule and use it if you want to override
  42 //something, but do not change the name of _Module
  43 extern CComModule _Module;
  44 #include <atlcom.h>
  45 #include <atlwin.h>
  46 
  47 #include <atlhost.h>
  48 #include <commdlg.h>
  49 #include <commctrl.h>
  50 #include <windowsx.h>
  51 #include <urlmon.h>
  52 #include <wininet.h>
  53 #include <shellapi.h>
  54 #include <time.h>
  55 #include <math.h>
  56 #include <stdio.h>
  57 
  58 #include <jni.h>
  59 
  60 #include "resource.h"       // main symbols
  61 #include "DownloadHelper.h"
  62 
  63 DownloadHelper::DownloadHelper() {
  64 
  65     m_showProgressDialog = TRUE;
  66     m_pszURL = NULL;
  67     m_pszFileName = NULL;
  68     m_pszNameText = NULL;
  69 }
  70 
  71 DownloadHelper::~DownloadHelper() {
  72 
  73 }
  74 
  75 HRESULT DownloadHelper::doDownload() {
  76     return DownloadFile(m_pszURL, m_pszFileName, FALSE, m_showProgressDialog);
  77 }
  78 
  79 HRESULT DownloadHelper::DownloadFile(const TCHAR* szURL,
  80         const TCHAR* szLocalFile, BOOL bResumable, BOOL bUIFeedback) {
  81     HINTERNET hOpen = NULL;
  82     HINTERNET hConnect = NULL;
  83     HINTERNET hRequest = NULL;
  84     HANDLE hFile = INVALID_HANDLE_VALUE;
  85     DWORD dwDownloadError = 0;
  86     DWORD nContentLength = 0;
  87 
  88     /* Some of error messages use drive letter.
  89        Result is something like "(C:)".
  90        NB: Parentheses are added here because in some other places
  91            we same message but can not provide disk label info */
  92     TCHAR drivePath[5];
  93     /* assuming szLocalFile is not NULL */
  94     _sntprintf(drivePath, 5, "(%c:)", szLocalFile[0]);
  95     WCHAR* wName = CT2CW(drivePath);
  96 
  97     __try {
  98         m_csDownload.Lock();
  99 
 100         time(&m_startTime);
 101 
 102     }
 103     __finally {
 104         m_csDownload.Unlock();
 105     }
 106 
 107     __try {
 108         // block potential security hole
 109         if (strstr(szURL, TEXT("file://")) != NULL) {
 110             dwDownloadError = 1;
 111             __leave;
 112         }
 113 
 114         HWND hProgressInfo = NULL;
 115         TCHAR szStatus[BUFFER_SIZE];
 116 
 117         if (bUIFeedback) {
 118             // init download dialg text
 119             m_dlg->initDialogText(m_pszURL, m_pszNameText);
 120         }
 121 
 122         // Open Internet Call
 123         hOpen = ::InternetOpen("deployHelper", INTERNET_OPEN_TYPE_PRECONFIG,
 124                 NULL, NULL, NULL);
 125 
 126         if (hOpen == NULL) {
 127             dwDownloadError = 1;
 128             __leave;
 129         }
 130 
 131         // URL components
 132         URL_COMPONENTS url_components;
 133         ::ZeroMemory(&url_components, sizeof(URL_COMPONENTS));
 134 
 135         TCHAR szHostName[BUFFER_SIZE], szUrlPath[BUFFER_SIZE],
 136                 szExtraInfo[BUFFER_SIZE];
 137         url_components.dwStructSize = sizeof(URL_COMPONENTS);
 138         url_components.lpszHostName = szHostName;
 139         url_components.dwHostNameLength = BUFFER_SIZE;
 140         url_components.nPort = NULL;
 141         url_components.lpszUrlPath = szUrlPath;
 142         url_components.dwUrlPathLength = BUFFER_SIZE;
 143         url_components.lpszExtraInfo = szExtraInfo;
 144         url_components.dwExtraInfoLength = BUFFER_SIZE;
 145 
 146         // Crack the URL into pieces
 147         ::InternetCrackUrl(szURL, lstrlen(szURL), NULL, &url_components);
 148 
 149         // Open Internet Connection
 150         hConnect = ::InternetConnect(hOpen, url_components.lpszHostName,
 151                 url_components.nPort, "", "", INTERNET_SERVICE_HTTP, NULL,
 152                 NULL);
 153 
 154         if (hConnect == NULL) {
 155             dwDownloadError = 1;
 156             __leave;
 157         }
 158 
 159         // Determine the relative URL path by combining
 160         // Path and ExtraInfo
 161         char szURL[4096];
 162 
 163         if (url_components.dwUrlPathLength !=  0)
 164             lstrcpy(szURL, url_components.lpszUrlPath);
 165         else
 166             lstrcpy(szURL, "/");
 167 
 168         if (url_components.dwExtraInfoLength != 0)
 169             lstrcat(szURL, url_components.lpszExtraInfo);
 170 
 171         BOOL bRetryHttpRequest = FALSE;
 172         int numberOfRetry = 0;
 173         long secondsToWait = 60;
 174 
 175         do {
 176             bRetryHttpRequest = FALSE;
 177 
 178             // Make a HTTP GET request
 179             hRequest = ::HttpOpenRequest(hConnect, "GET", szURL, "HTTP/1.1",
 180                     "", NULL,
 181                     INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_DONT_CACHE,
 182                     0);
 183 
 184             if (hRequest == NULL) {
 185                 dwDownloadError = 1;
 186                 __leave;
 187             }
 188 
 189             // Create or open existing destination file
 190             hFile = ::CreateFile(szLocalFile, GENERIC_WRITE, 0, NULL,
 191                     OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
 192 
 193             if (hFile == INVALID_HANDLE_VALUE) {
 194                 if (bUIFeedback) {
 195                     if (IDRETRY == m_dlg->SafeMessageBox(
 196                                             IDS_DISK_WRITE_ERROR,
 197                                             IDS_DISK_WRITE_ERROR_CAPTION,
 198                                             IDS_ERROR_CAPTION,
 199                                             DIALOG_ERROR_RETRYCANCEL,
 200                                             wName)) {
 201                          bRetryHttpRequest = TRUE;
 202                          continue;
 203                     }
 204                 }
 205                 dwDownloadError = 1;
 206                 __leave;
 207             }
 208             DWORD fileSize = GetFileSize(hFile, NULL);
 209 
 210             // Check if resumable download is enabled
 211             if (bResumable == FALSE) {
 212                 // Start from scratch
 213                 fileSize = 0;
 214             }
 215 
 216             FILETIME tWrite;
 217             BOOL rangereq = FALSE;
 218             if ((fileSize != 0) && (fileSize != 0xFFFFFFFF) &&
 219                     GetFileTime(hFile, NULL, NULL, &tWrite)) {
 220                 char szHead[100];
 221                 SYSTEMTIME tLocal;
 222                 char buf[INTERNET_RFC1123_BUFSIZE];
 223 
 224                 FileTimeToSystemTime(&tWrite, &tLocal);
 225                 InternetTimeFromSystemTime(&tLocal, INTERNET_RFC1123_FORMAT,
 226                         buf, INTERNET_RFC1123_BUFSIZE);
 227                 sprintf(szHead, "Range: bytes=%d-\r\nIf-Range: %s\r\n",
 228                         fileSize, buf);
 229                 HttpAddRequestHeaders(hRequest, szHead, lstrlen(szHead),
 230                         HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE);
 231                 rangereq = TRUE;
 232             }
 233 
 234             // This is a loop to handle various potential error when the
 235             // connection is made
 236             BOOL bCont = TRUE;
 237 
 238             while ((FALSE == ::HttpSendRequest(hRequest, NULL, NULL, NULL, NULL))
 239             && bCont ) {
 240                 // We might have an invalid CA.
 241                 DWORD dwErrorCode = GetLastError();
 242 
 243                 switch(dwErrorCode) {
 244                     case E_JDHELPER_TIMEOUT:
 245                     case E_JDHELPER_NAME_NOT_RESOLVED:
 246                     case E_JDHELPER_CANNOT_CONNECT: {
 247                         bCont = FALSE;
 248                         // Display the information dialog
 249                         if (bUIFeedback) {
 250                             // decrement download counter to prevent progress
 251                             // dialog from popping up while the message box is
 252                             // up
 253                             m_dlg->bundleInstallComplete();
 254                             if (dwErrorCode == E_JDHELPER_TIMEOUT) {
 255                                 bRetryHttpRequest =
 256                                     (IDRETRY == m_dlg->SafeMessageBox(
 257                                        IDS_HTTP_STATUS_REQUEST_TIMEOUT,
 258                                        IDS_HTTP_INSTRUCTION_REQUEST_TIMEOUT,
 259                                        IDS_ERROR_CAPTION,
 260                                        DIALOG_ERROR_RETRYCANCEL));
 261                             } else {
 262                                 bRetryHttpRequest =
 263                                     (IDRETRY == m_dlg->SafeMessageBox(
 264                                        IDS_HTTP_STATUS_SERVER_NOT_REACHABLE,
 265                                        IDS_HTTP_INSTRUCTION_SERVER_NOT_REACHABLE,
 266                                        IDS_ERROR_CAPTION,
 267                                        DIALOG_ERROR_RETRYCANCEL));
 268                             }
 269                             // re-increment counter because it will be decremented
 270                             // again upon return
 271                             m_dlg->bundleInstallStart();
 272                             bCont = bRetryHttpRequest;
 273                         }
 274                         break;
 275                     }
 276                     case ERROR_INTERNET_INVALID_CA:
 277                     case ERROR_INTERNET_SEC_CERT_CN_INVALID:
 278                     case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
 279                     case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
 280                     case ERROR_INTERNET_INCORRECT_PASSWORD:
 281                     case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
 282                     default: {
 283                         // Unless the user agrees to continue, we just
 284                         // abandon now !
 285                         bCont = FALSE;
 286 
 287                         // Make sure to test the return code from
 288                         // InternetErrorDlg user may click OK or Cancel. In
 289                         // case of Cancel, request should not be resubmitted
 290                         if (bUIFeedback) {
 291                             if (ERROR_SUCCESS == ::InternetErrorDlg(
 292                                     NULL, hRequest,
 293                                     dwErrorCode,
 294                                     FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
 295                                     FLAGS_ERROR_UI_FLAGS_GENERATE_DATA |
 296                                     FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
 297                                     NULL))
 298                                 bCont = TRUE;
 299                         }
 300                     }
 301                 }
 302             }
 303 
 304             if (bCont == FALSE) {
 305                 // User has denied the request
 306                 dwDownloadError = 1;
 307                 __leave;
 308             }
 309 
 310             //
 311             // Read HTTP status code
 312             //
 313             DWORD dwErrorCode = GetLastError();
 314             DWORD dwStatus=0;
 315             DWORD dwStatusSize = sizeof(DWORD);
 316 
 317             if (FALSE == ::HttpQueryInfo(hRequest, HTTP_QUERY_FLAG_NUMBER |
 318                     HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL)) {
 319                 dwErrorCode = GetLastError();
 320             }
 321 
 322             bCont = TRUE;
 323             while ((dwStatus == HTTP_STATUS_PROXY_AUTH_REQ ||
 324                     dwStatus == HTTP_STATUS_DENIED) &&
 325                     bCont) {
 326                 int result = ::InternetErrorDlg(GetDesktopWindow(), hRequest, ERROR_INTERNET_INCORRECT_PASSWORD,
 327                         FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
 328                         FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
 329                         FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
 330                         NULL);
 331                 if (ERROR_CANCELLED == result) {
 332                     bCont = FALSE;
 333                 }
 334                 else {
 335                     ::HttpSendRequest(hRequest, NULL, 0, NULL, 0);
 336 
 337                     // Reset buffer length
 338                     dwStatusSize = sizeof(DWORD);
 339 
 340                     ::HttpQueryInfo(hRequest, HTTP_QUERY_FLAG_NUMBER |
 341                             HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize,
 342                             NULL);
 343                 }
 344             }
 345 
 346             if (dwStatus == HTTP_STATUS_OK ||
 347                     dwStatus == HTTP_STATUS_PARTIAL_CONTENT) {
 348                 // Determine content length, so we may show the progress bar
 349                 // meaningfully
 350                 //
 351                 nContentLength = 0;
 352                 DWORD nLengthSize = sizeof(DWORD);
 353                 ::HttpQueryInfo(hRequest,
 354                         HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
 355                         &nContentLength, &nLengthSize, NULL);
 356 
 357                 if (nContentLength <= 0) {
 358                     // If can't estimate content length, estimate it
 359                     // to be 6MB
 360                     nContentLength = 15000000;
 361                 }
 362                 else if (rangereq && (fileSize != 0) &&
 363                         (nContentLength == fileSize)) {
 364                     // If the file is already downloaded completely and then
 365                     // we send a range request, the whole file is sent instead
 366                     // of nothing. So avoid downloading again.
 367                     // Some times return value is 206, even when whole file
 368                     // is sent. So check if "Content-range:" is present in the
 369                     // reply
 370                     char buffer[256];
 371                     DWORD length = sizeof(buffer);
 372                     if(!HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_RANGE,
 373                             buffer, &length, NULL)) {
 374                         if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED,
 375                                 buffer, &length, NULL)) {
 376                             SYSTEMTIME systime;
 377                             FILETIME filtime;
 378                             InternetTimeToSystemTime(buffer, &systime, NULL);
 379                             SystemTimeToFileTime(&systime, &filtime);
 380                             if ((CompareFileTime(&tWrite, &filtime)) == 1) {
 381                                 // no need to download
 382                                 dwDownloadError = 0;
 383                                 __leave;
 384                             }
 385                         }
 386                         else {
 387                             ::SetFilePointer(hFile, 0, 0, FILE_BEGIN);
 388                             ::SetEndOfFile(hFile); // truncate the file
 389                         }
 390                     }
 391 
 392                 }
 393 
 394                 TCHAR szBuffer[8096];
 395                 DWORD dwBufferSize = 8096;
 396 
 397                 // Read from HTTP connection and write into
 398                 // destination file
 399                 //
 400                 DWORD nRead = 0;
 401                 DWORD dwTotalRead = 0;
 402                 BOOL bCancel = FALSE;
 403 
 404                 if (dwStatus == HTTP_STATUS_PARTIAL_CONTENT) {
 405                     // If we are using resumable download, fake
 406                     // start time so it looks like we have begun
 407                     // the download several minutes again.
 408                     //
 409                     m_startTime = m_startTime - 100;
 410 
 411                     ::SetFilePointer(hFile, 0, 0, FILE_END); // seek to end
 412                 }
 413                 else {
 414                     ::SetFilePointer(hFile, 0, 0, FILE_BEGIN);
 415                     ::SetEndOfFile(hFile); // truncate the file
 416                 }
 417 
 418                 do {
 419                     nRead=0;
 420 
 421                     if (::InternetReadFile(hRequest, szBuffer, dwBufferSize,
 422                             &nRead)) {
 423                         if (nRead) {
 424                             DWORD dwNumberOfBytesWritten = NULL;
 425 
 426                             BOOL ret = WriteFile(hFile, szBuffer, nRead,
 427                                     &dwNumberOfBytesWritten, NULL);
 428 
 429                             if (!ret) {
 430                                 // WriteFile failed
 431                                 if (bUIFeedback) {
 432                                     if (GetLastError() == ERROR_DISK_FULL) {
 433                                        bRetryHttpRequest =
 434                                             (IDRETRY == m_dlg->SafeMessageBox(
 435                                             IDS_DISK_FULL_ERROR,
 436                                             IDS_DISK_FULL_ERROR_CAPTION,
 437                                             IDS_ERROR_CAPTION,
 438                                             DIALOG_ERROR_RETRYCANCEL,
 439                                             wName));
 440                                     } else {
 441                                         bRetryHttpRequest =
 442                                             (IDRETRY == m_dlg->SafeMessageBox(
 443                                             IDS_DISK_WRITE_ERROR,
 444                                             IDS_DISK_WRITE_ERROR_CAPTION,
 445                                             IDS_ERROR_CAPTION,
 446                                             DIALOG_ERROR_RETRYCANCEL,
 447                                             wName));
 448                                     }
 449                                     if (!bRetryHttpRequest) {
 450                                         dwDownloadError = 1;
 451                                         break;
 452                                     }
 453                                 }
 454                                 continue;
 455                             }
 456                         }
 457 
 458                         dwTotalRead += nRead;
 459 
 460                         // update download progress dialog
 461                         m_dlg->OnProgress(nRead);
 462                         // Check if download has been cancelled
 463                         if (m_dlg->isDownloadCancelled()) {
 464                             m_dlg->decrementProgressMax(nContentLength,
 465                                     dwTotalRead);
 466                             bCancel = TRUE;
 467                             break;
 468                         }
 469 
 470                     }
 471                     else {
 472                         bCancel = TRUE;
 473                         break;
 474                     }
 475                 }
 476                 while (nRead);
 477 
 478 
 479                 if (bCancel) {
 480                     // User has cancelled the operation or InternetRead failed
 481                     // don't do return here, we need to cleanup
 482                     dwDownloadError = 1;
 483                     __leave;
 484                 }
 485             }
 486             else if (dwStatus == 416 && (fileSize != 0) &&
 487                     (fileSize != 0xFFFFFFFF)) {
 488                 // This error could be returned, When the full file exists
 489                 // and a range request is sent with range beyond filessize.
 490                 // The best way to fix this is in future is, to send HEAD
 491                 // request and get filelength before sending range request.
 492                 dwDownloadError = 0;
 493                 __leave;
 494             }
 495             else if (dwStatus == 403) { // Forbidden from Akamai means we need to get a new download token
 496                 JNIEnv *env = m_dlg->getJNIEnv();
 497                 jclass exceptionClass = env->FindClass("java/net/HttpRetryException");
 498                 if (exceptionClass == NULL) {
 499                     /* Unable to find the exception class, give up. */
 500                     __leave;
 501                 }
 502                 jmethodID constructor;
 503                 constructor = env->GetMethodID(exceptionClass,
 504                                "<init>", "(Ljava/lang/String;I)V");
 505                 if (constructor != NULL) {
 506                     jobject exception = env->NewObject(exceptionClass,
 507                             constructor, env->NewStringUTF("Forbidden"),
 508                             403);
 509                     env->Throw((jthrowable) exception);
 510                 }
 511                 __leave;
 512             }
 513             else if(dwStatus >= 400 && dwStatus < 600) {
 514                 /* NB: Following case seems to be never used!
 515 
 516                    HTTP_STATUS_FORBIDDEN is the same as 403 and
 517                    403 was specially handled few lines above! */
 518                 if (dwStatus == HTTP_STATUS_FORBIDDEN) {
 519                     if (bUIFeedback) {
 520                         bRetryHttpRequest = (IDRETRY == m_dlg->SafeMessageBox(
 521                                             IDS_HTTP_STATUS_FORBIDDEN,
 522                                             IDS_HTTP_INSTRUCTION_FORBIDDEN,
 523                                             IDS_ERROR_CAPTION,
 524                                             DIALOG_ERROR_RETRYCANCEL,
 525                                             L"403"));
 526                     }
 527                 }
 528                 else if (dwStatus == HTTP_STATUS_SERVER_ERROR) {
 529                     if (bUIFeedback) {
 530                        bRetryHttpRequest = (IDRETRY == m_dlg->SafeMessageBox(
 531                                             IDS_HTTP_STATUS_SERVER_ERROR,
 532                                             IDS_HTTP_INSTRUCTION_UNKNOWN_ERROR,
 533                                             IDS_ERROR_CAPTION,
 534                                             DIALOG_ERROR_RETRYCANCEL,
 535                                             L"500"));
 536                     }
 537                 }
 538                 else if (dwStatus == HTTP_STATUS_SERVICE_UNAVAIL) {
 539                     if (numberOfRetry < 5) {
 540                         // If the server is busy, automatically retry
 541 
 542                         // We wait couple seconds before retry to avoid
 543                         // congestion
 544                         for (long i = (long) secondsToWait; i >= 0; i--) {
 545                             // Update status
 546                             if (bUIFeedback) {
 547                                 char szBuffer[BUFFER_SIZE];
 548                                 ::LoadString(_Module.GetResourceInstance(),
 549                                         IDS_DOWNLOAD_STATUS_RETRY, szStatus,
 550                                         BUFFER_SIZE);
 551                                 wsprintf(szBuffer, szStatus, i);
 552 
 553                                 ::SetWindowText(hProgressInfo, szBuffer);
 554                             }
 555 
 556                             // Sleep 1 second
 557                             ::Sleep(1000);
 558                         }
 559 
 560                         // We use a semi-binary backoff algorithm to
 561                         // determine seconds to wait
 562                         numberOfRetry += 1;
 563                         secondsToWait = secondsToWait + 30;
 564                         bRetryHttpRequest = TRUE;
 565 
 566                         continue;
 567                     }
 568                     else {
 569                         if (bUIFeedback) {
 570                             bRetryHttpRequest = (IDRETRY == m_dlg->SafeMessageBox(
 571                                             IDS_HTTP_STATUS_SERVICE_UNAVAIL,
 572                                             IDS_HTTP_INSTRUCTION_SERVICE_UNAVAIL,
 573                                             IDS_ERROR_CAPTION,
 574                                             DIALOG_ERROR_RETRYCANCEL,
 575                                             L"503"));
 576 
 577                             if (bRetryHttpRequest) {
 578                                 numberOfRetry = 0;
 579                                 secondsToWait = 60;
 580                                 continue;
 581                             }
 582                         }
 583                     }
 584                 }
 585                 else {
 586                     if (bUIFeedback) {
 587                         WCHAR szBuffer[10];
 588                         _snwprintf(szBuffer, 10, L"%d", dwStatus);
 589                         bRetryHttpRequest = (IDRETRY == m_dlg->SafeMessageBox(
 590                                             IDS_HTTP_STATUS_OTHER,
 591                                             IDS_HTTP_INSTRUCTION_UNKNOWN_ERROR,
 592                                             IDS_ERROR_CAPTION,
 593                                             DIALOG_ERROR_RETRYCANCEL,
 594                                             szBuffer));
 595                     }
 596                 }
 597                 if (!bRetryHttpRequest) {
 598                     dwDownloadError = 1;
 599                 }
 600             }
 601             else {
 602                 if (bUIFeedback) {
 603                     WCHAR szBuffer[10];
 604                     _snwprintf(szBuffer, 10, L"%d", dwStatus);
 605                     bRetryHttpRequest = (IDRETRY == m_dlg->SafeMessageBox(
 606                                             IDS_HTTP_STATUS_OTHER,
 607                                             IDS_HTTP_INSTRUCTION_UNKNOWN_ERROR,
 608                                             IDS_ERROR_CAPTION,
 609                                             DIALOG_ERROR_RETRYCANCEL,
 610                                             szBuffer));
 611                 }
 612                 if (!bRetryHttpRequest) {
 613                     dwDownloadError = 1;
 614                 }
 615             }
 616 
 617 
 618 
 619             // Close HTTP request
 620             //
 621             // This is necessary if the HTTP request
 622             // is retried
 623             if (hRequest)
 624                 ::InternetCloseHandle(hRequest);
 625             if (hFile != INVALID_HANDLE_VALUE) {
 626                 ::CloseHandle(hFile);
 627                 hFile = INVALID_HANDLE_VALUE;
 628             }
 629         }
 630         while (bRetryHttpRequest);
 631     }
 632     __finally {
 633         if (hRequest)
 634             ::InternetCloseHandle(hRequest);
 635 
 636         if (hConnect)
 637             ::InternetCloseHandle(hConnect);
 638 
 639         if (hOpen)
 640             ::InternetCloseHandle(hOpen);
 641 
 642         if (hFile != INVALID_HANDLE_VALUE)
 643             ::CloseHandle(hFile);
 644     }
 645 
 646 
 647 
 648     // Exit dialog
 649     if (dwDownloadError == 0) {
 650         return S_OK;
 651     } else {
 652         DeleteFile(szLocalFile);
 653         return E_FAIL;
 654     }
 655 }