jdk/src/windows/native/sun/jkernel/DownloadHelper.cpp
changeset 8197 e45f21c2a40b
parent 7867 f83cd8bd35c6
child 8198 aca2f99e4b52
equal deleted inserted replaced
7867:f83cd8bd35c6 8197:e45f21c2a40b
     1 /*
       
     2  * Copyright (c) 2008, 2010, 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 }