+HINTERNET pkgInternetAgent::OpenURL( const char *URL )
+{
+ /* Open an internet data stream.
+ */
+ HINTERNET ResourceHandle;
+
+ /* This requires an internet
+ * connection to have been established...
+ */
+ if( (SessionHandle == NULL)
+ && (InternetAttemptConnect( 0 ) == ERROR_SUCCESS) )
+ /*
+ * ...so, on first call, we perform the connection setup
+ * which we deferred from the class constructor; (MSDN
+ * cautions that this MUST NOT be done in the constructor
+ * for any global class object such as ours).
+ */
+ SessionHandle = InternetOpen
+ ( "MinGW Installer", INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL, NULL, 0
+ );
+
+ /* Aggressively attempt to acquire a resource handle, which we may use
+ * to access the specified URL; (schedule a maximum of five attempts).
+ */
+ int retries = 5;
+ do { ResourceHandle = InternetOpenUrl
+ (
+ /* Here, we attempt to assign a URL specific resource handle,
+ * within the scope of the SessionHandle obtained above, to
+ * manage the connection for the requested URL.
+ *
+ * Note: Scott Michel suggests INTERNET_FLAG_EXISTING_CONNECT
+ * here; MSDN tells us it is useful only for FTP connections.
+ * Since we are primarily interested in HTTP connections, it
+ * may not help us. However, it does no harm, and MSDN isn't
+ * always the reliable source of information we might like.
+ * Persistent HTTP connections aren't entirely unknown, (and
+ * indeed, MSDN itself tells us we need to use one, when we
+ * negotiate proxy authentication); thus, we may just as well
+ * specify it anyway, on the off-chance that it may introduce
+ * an undocumented benefit beyond wishful thinking.
+ */
+ SessionHandle, URL, NULL, 0, INTERNET_FLAG_EXISTING_CONNECT, 0
+ );
+ if( ResourceHandle == NULL )
+ {
+ /* We failed to acquire a handle for the URL resource; we may retry
+ * unless we have exhausted the specified retry limit...
+ */
+ if( --retries < 1 )
+ /*
+ * ...in which case, we diagnose failure to open the URL.
+ */
+ dmh_notify( DMH_ERROR, "%s:cannot open URL\n", URL );
+
+ else DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_INTERNET_REQUESTS ),
+ dmh_printf( "%s\nConnecting ... failed(status=%u); retrying...\n",
+ URL, GetLastError() )
+ );
+ }
+ else
+ { /* We got a handle for the URL resource, but we cannot yet be sure
+ * that it is ready for use; we may still need to address a need for
+ * proxy or server authentication, or other temporary fault.
+ */
+ int retry = 5;
+ unsigned long ResourceStatus, ResourceErrno;
+ do { /* We must capture any error code which may have been returned,
+ * BEFORE we move on to evaluate the resource status, (since the
+ * procedure for checking status may change the error code).
+ */
+ ResourceErrno = GetLastError();
+ ResourceStatus = QueryStatus( ResourceHandle );
+ if( ResourceStatus == HTTP_STATUS_PROXY_AUTH_REQ )
+ {
+ /* We've identified a requirement for proxy authentication;
+ * here we simply hand the task off to the Microsoft handler,
+ * to solicit the appropriate response from the user.
+ *
+ * FIXME: this may be a reasonable approach when running in
+ * a GUI context, but is rather inelegant in the CLI context.
+ * Furthermore, this particular implementation provides only
+ * for proxy authentication, ignoring the possibility that
+ * server authentication may be required. We may wish to
+ * revisit this later.
+ */
+ unsigned long user_response;
+ do { user_response = InternetErrorDlg
+ ( dmh_dialogue_context(), ResourceHandle, ResourceErrno,
+ FLAGS_ERROR_UI_FILTER_FOR_ERRORS |
+ FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS |
+ FLAGS_ERROR_UI_FLAGS_GENERATE_DATA,
+ NULL
+ );
+ /* Having obtained authentication credentials from
+ * the user, we may retry the open URL request...
+ */
+ if( (user_response == ERROR_INTERNET_FORCE_RETRY)
+ && HttpSendRequest( ResourceHandle, NULL, 0, 0, 0 ) )
+ {
+ /* ...and, if successful...
+ */
+ ResourceErrno = GetLastError();
+ ResourceStatus = QueryStatus( ResourceHandle );
+ if( ResourceStatus == HTTP_STATUS_OK )
+ /*
+ * ...ensure that the response is anything but 'retry',
+ * so that we will break out of the retry loop...
+ */
+ user_response ^= -1L;
+ }
+ /* ...otherwise, we keep retrying when appropriate.
+ */
+ } while( user_response == ERROR_INTERNET_FORCE_RETRY );
+ }
+ else if( ResourceStatus != HTTP_STATUS_OK )
+ {
+ /* Other failure modes may not be so readily recoverable;
+ * with little hope of success, retry anyway.
+ */
+ DEBUG_INVOKE_IF( DEBUG_REQUEST( DEBUG_TRACE_INTERNET_REQUESTS ),
+ dmh_printf( "%s: abnormal request status = %u; retrying...\n",
+ URL, ResourceStatus
+ ));
+ if( HttpSendRequest( ResourceHandle, NULL, 0, 0, 0 ) )
+ ResourceStatus = QueryStatus( ResourceHandle );
+ }
+ } while( (ResourceStatus != HTTP_STATUS_OK) && (retry-- > 0) );
+
+ /* Confirm that the URL was (eventually) opened successfully...
+ */
+ if( ResourceStatus == HTTP_STATUS_OK )
+ /*
+ * ...in which case, we have no need to schedule any further
+ * retries.
+ */
+ retries = 0;
+
+ else
+ { /* The resource handle we've acquired isn't useable; discard it,
+ * so we can reclaim any resources associated with it.
+ */
+ Close( ResourceHandle );
+
+ /* When we have exhausted our retry limit...
+ */
+ if( --retries < 1 )
+ {
+ /* Give up; nullify our resource handle to indicate failure.
+ */
+ ResourceHandle = NULL;
+
+ /* Issue a diagnostic advising the user to refer the problem
+ * to the mingw-get maintainer for possible follow-up.
+ */
+ dmh_control( DMH_BEGIN_DIGEST );
+ dmh_notify( DMH_WARNING,
+ "%s: opened with unexpected status: code = %u\n",
+ URL, ResourceStatus
+ );
+ dmh_notify( DMH_WARNING,
+ "please report this to the mingw-get maintainer\n"
+ );
+ dmh_control( DMH_END_DIGEST );
+ }
+ }
+ }
+ /* If we haven't yet acquired a valid resource handle, and we haven't
+ * yet exhausted our retry limit, go back and try again.
+ */
+ } while( retries > 0 );
+
+ /* Ultimately, we return the resource handle for the opened URL,
+ * or NULL if the open request failed.
+ */
+ return ResourceHandle;
+}
+