1014 lines
22 KiB
C
1014 lines
22 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1998 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
TODO: cmntool.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
<TODO: fill in abstract>
|
||
|
|
||
|
Author:
|
||
|
|
||
|
TODO: <full name> (<alias>) <date>
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
<full name> (<alias>) <date> <comments>
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "pch.h"
|
||
|
#include "resource.h"
|
||
|
#include <wininet.h>
|
||
|
|
||
|
typedef enum {
|
||
|
DOWNLOAD_CONNECTING,
|
||
|
DOWNLOAD_GETTING_FILE,
|
||
|
DOWNLOAD_DISCONNECTING
|
||
|
} DOWNLOADSTATE;
|
||
|
|
||
|
|
||
|
|
||
|
typedef HINTERNET (WINAPI * INTERNETOPEN) (
|
||
|
IN LPCSTR lpszAgent,
|
||
|
IN DWORD dwAccessType,
|
||
|
IN LPCSTR lpszProxyName,
|
||
|
IN LPCSTR lpszProxyBypass,
|
||
|
IN DWORD dwFlags
|
||
|
);
|
||
|
|
||
|
typedef BOOL (WINAPI * INTERNETCLOSEHANDLE) (
|
||
|
IN HINTERNET Handle
|
||
|
);
|
||
|
|
||
|
typedef HINTERNET (WINAPI * INTERNETOPENURL) (
|
||
|
IN HINTERNET hInternetSession,
|
||
|
IN LPCSTR lpszUrl,
|
||
|
IN LPCSTR lpszHeaders,
|
||
|
IN DWORD dwHeadersLength,
|
||
|
IN DWORD dwFlags,
|
||
|
IN DWORD dwContext
|
||
|
);
|
||
|
|
||
|
typedef BOOL (WINAPI * INTERNETREADFILE) (
|
||
|
IN HINTERNET hFile,
|
||
|
IN LPVOID lpBuffer,
|
||
|
IN DWORD dwNumberOfBytesToRead,
|
||
|
OUT LPDWORD lpNumberOfBytesRead
|
||
|
);
|
||
|
|
||
|
typedef BOOL (WINAPI * INTERNETCANONICALIZEURLA) (
|
||
|
IN LPCSTR lpszUrl,
|
||
|
OUT LPSTR lpszBuffer,
|
||
|
IN OUT LPDWORD lpdwBufferLength,
|
||
|
IN DWORD dwFlags
|
||
|
);
|
||
|
|
||
|
typedef DWORD (WINAPI * INTERNETSETFILEPOINTER) (
|
||
|
IN HINTERNET hFile,
|
||
|
IN LONG lDistanceToMove,
|
||
|
IN PVOID pReserved,
|
||
|
IN DWORD dwMoveMethod,
|
||
|
IN DWORD dwContext
|
||
|
);
|
||
|
|
||
|
typedef BOOL (WINAPI * INTERNETHANGUP) (
|
||
|
IN DWORD dwConnection,
|
||
|
IN DWORD dwReserved
|
||
|
);
|
||
|
|
||
|
typedef DWORD (WINAPI * INTERNETDIALA) (
|
||
|
IN HWND hwndParent,
|
||
|
IN PCSTR lpszConnectoid,
|
||
|
IN DWORD dwFlags,
|
||
|
OUT LPDWORD lpdwConnection,
|
||
|
IN DWORD dwReserved
|
||
|
);
|
||
|
|
||
|
static HINSTANCE g_Lib;
|
||
|
static INTERNETOPEN g_InternetOpenA;
|
||
|
static INTERNETCLOSEHANDLE g_InternetCloseHandle;
|
||
|
static INTERNETOPENURL g_InternetOpenUrlA;
|
||
|
static INTERNETREADFILE g_InternetReadFile;
|
||
|
static INTERNETCANONICALIZEURLA g_InternetCanonicalizeUrlA;
|
||
|
static INTERNETSETFILEPOINTER g_InternetSetFilePointer;
|
||
|
static INTERNETDIALA g_InternetDialA;
|
||
|
static INTERNETHANGUP g_InternetHangUp;
|
||
|
|
||
|
static BOOL g_Dialed;
|
||
|
static DWORD g_Cxn;
|
||
|
|
||
|
HANDLE g_hHeap;
|
||
|
HINSTANCE g_hInst;
|
||
|
|
||
|
BOOL WINAPI MigUtil_Entry (HINSTANCE, DWORD, PVOID);
|
||
|
|
||
|
PCSTR g_AppName = TEXT("FTP Download Engine");
|
||
|
//PCSTR g_DirFile = TEXT("ftp://jimschm-dev/upgdir.inf");
|
||
|
PCSTR g_DirFile = TEXT("file://popcorn/public/jimschm/upgdir.inf");
|
||
|
|
||
|
BOOL
|
||
|
DownloadUpdates (
|
||
|
HANDLE CancelEvent, OPTIONAL
|
||
|
HANDLE WantToRetryEvent, OPTIONAL
|
||
|
HANDLE OkToRetryEvent, OPTIONAL
|
||
|
PCSTR *Url OPTIONAL
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
pDownloadUpdatesWithUi (
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
BOOL
|
||
|
pCallEntryPoints (
|
||
|
DWORD Reason
|
||
|
)
|
||
|
{
|
||
|
HINSTANCE Instance;
|
||
|
|
||
|
//
|
||
|
// Simulate DllMain
|
||
|
//
|
||
|
|
||
|
Instance = g_hInst;
|
||
|
|
||
|
//
|
||
|
// Initialize the common libs
|
||
|
//
|
||
|
|
||
|
if (!MigUtil_Entry (Instance, Reason, NULL)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// TODO: Add others here if needed (don't forget to prototype above)
|
||
|
//
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
Init (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
g_hHeap = GetProcessHeap();
|
||
|
g_hInst = GetModuleHandle (NULL);
|
||
|
|
||
|
return pCallEntryPoints (DLL_PROCESS_ATTACH);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
Terminate (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
pCallEntryPoints (DLL_PROCESS_DETACH);
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
HelpAndExit (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// This routine is called whenever command line args are wrong
|
||
|
//
|
||
|
|
||
|
_ftprintf (
|
||
|
stderr,
|
||
|
TEXT("Command Line Syntax:\n\n")
|
||
|
|
||
|
//
|
||
|
// TODO: Describe command line syntax(es), indent 2 spaces
|
||
|
//
|
||
|
|
||
|
TEXT(" cmntool [/F:file]\n")
|
||
|
|
||
|
TEXT("\nDescription:\n\n")
|
||
|
|
||
|
//
|
||
|
// TODO: Describe tool, indent 2 spaces
|
||
|
//
|
||
|
|
||
|
TEXT(" cmntool is a stub!\n")
|
||
|
|
||
|
TEXT("\nArguments:\n\n")
|
||
|
|
||
|
//
|
||
|
// TODO: Describe args, indent 2 spaces, say optional if necessary
|
||
|
//
|
||
|
|
||
|
TEXT(" /F Specifies optional file name\n")
|
||
|
|
||
|
);
|
||
|
|
||
|
exit (1);
|
||
|
}
|
||
|
|
||
|
|
||
|
INT
|
||
|
__cdecl
|
||
|
_tmain (
|
||
|
INT argc,
|
||
|
PCTSTR argv[]
|
||
|
)
|
||
|
{
|
||
|
INT i;
|
||
|
PCTSTR FileArg;
|
||
|
|
||
|
//
|
||
|
// TODO: Parse command line here
|
||
|
//
|
||
|
|
||
|
for (i = 1 ; i < argc ; i++) {
|
||
|
if (argv[i][0] == TEXT('/') || argv[i][0] == TEXT('-')) {
|
||
|
switch (_totlower (_tcsnextc (&argv[i][1]))) {
|
||
|
|
||
|
case TEXT('f'):
|
||
|
//
|
||
|
// Sample option - /f:file
|
||
|
//
|
||
|
|
||
|
if (argv[i][2] == TEXT(':')) {
|
||
|
FileArg = &argv[i][3];
|
||
|
} else if (i + 1 < argc) {
|
||
|
FileArg = argv[++i];
|
||
|
} else {
|
||
|
HelpAndExit();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
HelpAndExit();
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// Parse other args that don't require / or -
|
||
|
//
|
||
|
|
||
|
// None
|
||
|
HelpAndExit();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Begin processing
|
||
|
//
|
||
|
|
||
|
if (!Init()) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// TODO: Do work here
|
||
|
//
|
||
|
|
||
|
pDownloadUpdatesWithUi();
|
||
|
|
||
|
//
|
||
|
// End of processing
|
||
|
//
|
||
|
|
||
|
Terminate();
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
HANDLE CancelEvent;
|
||
|
HANDLE WantToRetryEvent;
|
||
|
HANDLE OkToRetryEvent;
|
||
|
HANDLE CloseEvent;
|
||
|
PCSTR Url;
|
||
|
} EVENTSTRUCT, *PEVENTSTRUCT;
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
CALLBACK
|
||
|
pUiDlgProc (
|
||
|
HWND hdlg,
|
||
|
UINT msg,
|
||
|
WPARAM wParam,
|
||
|
LPARAM lParam
|
||
|
)
|
||
|
{
|
||
|
static PEVENTSTRUCT eventStruct;
|
||
|
static UINT retries;
|
||
|
DWORD rc;
|
||
|
CHAR lastUrl[256];
|
||
|
CHAR text[256];
|
||
|
|
||
|
switch (msg) {
|
||
|
|
||
|
case WM_INITDIALOG:
|
||
|
eventStruct = (PEVENTSTRUCT) lParam;
|
||
|
retries = 0;
|
||
|
lastUrl[0] = 0;
|
||
|
SetTimer (hdlg, 1, 100, NULL);
|
||
|
break;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
switch (LOWORD(wParam)) {
|
||
|
|
||
|
case IDCANCEL:
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_MSG2), SW_HIDE);
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_URL), SW_HIDE);
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_RETRIES), SW_HIDE);
|
||
|
|
||
|
SetDlgItemTextA (hdlg, IDC_MSG1, "Stopping download...");
|
||
|
|
||
|
SetFocus (GetDlgItem (hdlg, IDC_MSG1));
|
||
|
EnableWindow (GetDlgItem (hdlg, IDCANCEL), FALSE);
|
||
|
|
||
|
SetEvent (eventStruct->CancelEvent);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WM_TIMER:
|
||
|
//
|
||
|
// Check the events
|
||
|
//
|
||
|
|
||
|
rc = WaitForSingleObject (eventStruct->WantToRetryEvent, 0);
|
||
|
|
||
|
if (rc == WAIT_OBJECT_0) {
|
||
|
//
|
||
|
// A download failed. Try again?
|
||
|
//
|
||
|
|
||
|
if (StringCompareA (lastUrl, eventStruct->Url)) {
|
||
|
retries = 0;
|
||
|
}
|
||
|
|
||
|
StackStringCopy (lastUrl, eventStruct->Url);
|
||
|
|
||
|
retries++;
|
||
|
|
||
|
if (retries > 5) {
|
||
|
//
|
||
|
// Too many retries -- give up!
|
||
|
//
|
||
|
|
||
|
SetEvent (eventStruct->CancelEvent);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// Retry
|
||
|
//
|
||
|
|
||
|
wsprintfA (text, "on attempt %u. Retrying.", retries);
|
||
|
SetDlgItemText (hdlg, IDC_RETRIES, text);
|
||
|
|
||
|
if (eventStruct->Url) {
|
||
|
SetDlgItemText (hdlg, IDC_URL, eventStruct->Url);
|
||
|
}
|
||
|
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_MSG2), SW_SHOW);
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_URL), SW_SHOW);
|
||
|
ShowWindow (GetDlgItem (hdlg, IDC_RETRIES), SW_SHOW);
|
||
|
|
||
|
SetEvent (eventStruct->OkToRetryEvent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rc = WaitForSingleObject (eventStruct->CloseEvent, 0);
|
||
|
|
||
|
if (rc == WAIT_OBJECT_0) {
|
||
|
EndDialog (hdlg, IDCANCEL);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
KillTimer (hdlg, 1);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
DWORD
|
||
|
WINAPI
|
||
|
pUiThread (
|
||
|
PVOID Arg
|
||
|
)
|
||
|
{
|
||
|
DialogBoxParam (
|
||
|
g_hInst,
|
||
|
(PCTSTR) IDD_STATUS,
|
||
|
NULL,
|
||
|
pUiDlgProc,
|
||
|
(LPARAM) Arg
|
||
|
);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HANDLE
|
||
|
pCreateUiThread (
|
||
|
PEVENTSTRUCT EventStruct
|
||
|
)
|
||
|
{
|
||
|
HANDLE h;
|
||
|
DWORD threadId;
|
||
|
|
||
|
h = CreateThread (NULL, 0, pUiThread, EventStruct, 0, &threadId);
|
||
|
|
||
|
return h;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pDownloadUpdatesWithUi (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
EVENTSTRUCT es;
|
||
|
BOOL b = FALSE;
|
||
|
HANDLE h;
|
||
|
|
||
|
//
|
||
|
// Create the events
|
||
|
//
|
||
|
|
||
|
es.CancelEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||
|
es.WantToRetryEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||
|
es.OkToRetryEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||
|
es.CloseEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
|
||
|
|
||
|
es.Url = NULL;
|
||
|
|
||
|
if (!es.CancelEvent || !es.WantToRetryEvent ||
|
||
|
!es.OkToRetryEvent || !es.CloseEvent
|
||
|
) {
|
||
|
DEBUGMSG ((DBG_ERROR, "Can't create events"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Start the UI
|
||
|
//
|
||
|
|
||
|
h = pCreateUiThread (&es);
|
||
|
|
||
|
if (!h) {
|
||
|
DEBUGMSG ((DBG_ERROR, "Can't create UI thread"));
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// Perform the download
|
||
|
//
|
||
|
|
||
|
b = DownloadUpdates (
|
||
|
es.CancelEvent,
|
||
|
es.WantToRetryEvent,
|
||
|
es.OkToRetryEvent,
|
||
|
&es.Url
|
||
|
);
|
||
|
|
||
|
//
|
||
|
// End the UI
|
||
|
//
|
||
|
|
||
|
SetEvent (es.CloseEvent);
|
||
|
WaitForSingleObject (h, INFINITE);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Cleanup & exit
|
||
|
//
|
||
|
|
||
|
CloseHandle (es.CancelEvent);
|
||
|
CloseHandle (es.WantToRetryEvent);
|
||
|
CloseHandle (es.OkToRetryEvent);
|
||
|
CloseHandle (es.CloseEvent);
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pOpenWinInetSupport (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
g_Lib = LoadLibrary (TEXT("wininet.dll"));
|
||
|
|
||
|
if (!g_Lib) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
(FARPROC) g_InternetOpenA = GetProcAddress (g_Lib, "InternetOpenA");
|
||
|
(FARPROC) g_InternetCloseHandle = GetProcAddress (g_Lib, "InternetCloseHandle");
|
||
|
(FARPROC) g_InternetOpenUrlA = GetProcAddress (g_Lib, "InternetOpenUrlA");
|
||
|
(FARPROC) g_InternetReadFile = GetProcAddress (g_Lib, "InternetReadFile");
|
||
|
(FARPROC) g_InternetCanonicalizeUrlA = GetProcAddress (g_Lib, "InternetCanonicalizeUrlA");
|
||
|
(FARPROC) g_InternetSetFilePointer = GetProcAddress (g_Lib, "InternetSetFilePointer");
|
||
|
(FARPROC) g_InternetHangUp = GetProcAddress (g_Lib, "InternetHangUp");
|
||
|
(FARPROC) g_InternetDialA = GetProcAddress (g_Lib, "InternetDialA");
|
||
|
|
||
|
if (!g_InternetOpenA || !g_InternetOpenUrlA || !g_InternetReadFile ||
|
||
|
!g_InternetCloseHandle || !g_InternetCanonicalizeUrlA ||
|
||
|
!g_InternetSetFilePointer || !g_InternetDialA || !g_InternetHangUp
|
||
|
) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pCloseWinInetSupport (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
FreeLibrary (g_Lib);
|
||
|
g_Lib = NULL;
|
||
|
g_InternetOpenA = NULL;
|
||
|
g_InternetOpenUrlA = NULL;
|
||
|
g_InternetReadFile = NULL;
|
||
|
g_InternetCloseHandle = NULL;
|
||
|
g_InternetCanonicalizeUrlA = NULL;
|
||
|
g_InternetSetFilePointer = NULL;
|
||
|
g_InternetDialA = NULL;
|
||
|
g_InternetHangUp = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pDownloadFile (
|
||
|
HINTERNET Session,
|
||
|
PCSTR RemoteFileUrl,
|
||
|
PCSTR LocalFile,
|
||
|
HANDLE CancelEvent
|
||
|
)
|
||
|
{
|
||
|
HINTERNET connection;
|
||
|
PBYTE buffer = NULL;
|
||
|
UINT size = 65536;
|
||
|
DWORD bytesRead;
|
||
|
DWORD dontCare;
|
||
|
HANDLE file = INVALID_HANDLE_VALUE;
|
||
|
BOOL b = FALSE;
|
||
|
|
||
|
//
|
||
|
// Establish connection to the file
|
||
|
//
|
||
|
|
||
|
connection = g_InternetOpenUrlA (
|
||
|
Session,
|
||
|
RemoteFileUrl,
|
||
|
NULL,
|
||
|
0,
|
||
|
INTERNET_FLAG_RELOAD, //INTERNET_FLAG_NO_UI
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (!connection) {
|
||
|
DEBUGMSGA ((DBG_ERROR "Can't connect to %s", RemoteFileUrl));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
|
||
|
//
|
||
|
// Create the local file
|
||
|
//
|
||
|
|
||
|
file = CreateFileA (
|
||
|
LocalFile,
|
||
|
GENERIC_WRITE,
|
||
|
0,
|
||
|
NULL,
|
||
|
CREATE_ALWAYS,
|
||
|
FILE_ATTRIBUTE_NORMAL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
if (file == INVALID_HANDLE_VALUE) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't create %s", LocalFile));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate a big buffer for downloading
|
||
|
//
|
||
|
|
||
|
buffer = MemAlloc (g_hHeap, 0, size);
|
||
|
if (!buffer) {
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Download the file
|
||
|
//
|
||
|
|
||
|
for (;;) {
|
||
|
|
||
|
if (WAIT_OBJECT_0 == WaitForSingleObject (CancelEvent, 0)) {
|
||
|
DEBUGMSG ((DBG_VERBOSE, "User cancellation detected"));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (!g_InternetReadFile (connection, buffer, size, &bytesRead)) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Error downloading %s", RemoteFileUrl));
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (!bytesRead) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!WriteFile (file, buffer, bytesRead, &dontCare, NULL)) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Error writing to %s", LocalFile));
|
||
|
__leave;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
b = TRUE;
|
||
|
}
|
||
|
__finally {
|
||
|
|
||
|
g_InternetCloseHandle (connection);
|
||
|
|
||
|
CloseHandle (file);
|
||
|
|
||
|
if (!b) {
|
||
|
DeleteFileA (LocalFile);
|
||
|
}
|
||
|
|
||
|
if (buffer) {
|
||
|
MemFree (g_hHeap, 0, buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pDownloadFileWithRetry (
|
||
|
IN HINTERNET Session,
|
||
|
IN PCSTR Url,
|
||
|
IN PCSTR DestFile,
|
||
|
IN HANDLE CancelEvent, OPTIONAL
|
||
|
IN HANDLE WantToRetryEvent, OPTIONAL
|
||
|
IN HANDLE OkToRetryEvent OPTIONAL
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
pDownloadFileWithRetry downloads a URL to a local file, as specified by the
|
||
|
caller. If CancelEvent is specified, then the caller can stop the download
|
||
|
by setting the event.
|
||
|
|
||
|
This function implements a retry mechanism via events. If the caller
|
||
|
specifies WantToRetryEvent and OkToRetryEvent, then this routine will allow
|
||
|
the caller an opportunity to retry a failed download.
|
||
|
|
||
|
The retry protocol is as follows:
|
||
|
|
||
|
- Caller establishes a wait on WantToRetryEvent, then calls DownloadUpdates
|
||
|
- Error occurs downloading one of the files
|
||
|
- WantToRetryEvent is set by this routine
|
||
|
- Caller's wait wakes up
|
||
|
- Caller asks user if they want to retry
|
||
|
- Caller sets CancelEvent or OkToRetryEvent, depending on user choice
|
||
|
- This routine wakes up and either retries or aborts
|
||
|
|
||
|
The caller must create all three events as auto-reset events in the
|
||
|
non-signaled state.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Session - Specifies the handle to an open internet session.
|
||
|
Url - Specifies the URL to download.
|
||
|
DestFile - Specifies the local path to download the file to.
|
||
|
CancelEvent - Specifies the handle to a caller-owned event. When this
|
||
|
event is set, the function will return FALSE and
|
||
|
GetLastError will return ERROR_CANCELLED.
|
||
|
WantToRetryEvent - Specifies the caller-owned event that is set when a
|
||
|
download error occurs. The caller should be waiting on
|
||
|
this event before calling DownloadUpdates.
|
||
|
OkToRetry - Specifies the caller-owned event that will be set in
|
||
|
response to a user's request to retry.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if the file was downloaded, FALSE if the user decides to cancel the
|
||
|
download.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
BOOL fail;
|
||
|
HANDLE waitArray[2];
|
||
|
DWORD rc;
|
||
|
|
||
|
//
|
||
|
// Loop until success, user decides to cancel, or user decides
|
||
|
// not to retry on error
|
||
|
//
|
||
|
|
||
|
for (;;) {
|
||
|
|
||
|
fail = FALSE;
|
||
|
|
||
|
if (!pDownloadFile (Session, Url, DestFile, CancelEvent)) {
|
||
|
|
||
|
fail = TRUE;
|
||
|
|
||
|
if (GetLastError() != ERROR_CANCELLED &&
|
||
|
CancelEvent && WantToRetryEvent && OkToRetryEvent
|
||
|
) {
|
||
|
|
||
|
//
|
||
|
// We set the WantToRetryEvent. The UI thread should
|
||
|
// be waiting on this. The UI thread will then ask
|
||
|
// the user if they want to retry or cancel. If the
|
||
|
// user wants to retry, the UI thread will set the
|
||
|
// OkToRetryEvent. If the user wants to cancel, the
|
||
|
// UI thread will set the CancelEvent.
|
||
|
//
|
||
|
|
||
|
SetEvent (WantToRetryEvent);
|
||
|
|
||
|
waitArray[0] = CancelEvent;
|
||
|
waitArray[1] = OkToRetryEvent;
|
||
|
|
||
|
rc = WaitForMultipleObjects (2, waitArray, FALSE, INFINITE);
|
||
|
|
||
|
if (rc == WAIT_OBJECT_0 + 1) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We fail
|
||
|
//
|
||
|
|
||
|
SetLastError (ERROR_CANCELLED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return !fail;
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
pGoOffline (
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
if (g_Dialed) {
|
||
|
g_Dialed = FALSE;
|
||
|
|
||
|
g_InternetHangUp (g_Cxn, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
pGoOnline (
|
||
|
HINTERNET Session
|
||
|
)
|
||
|
{
|
||
|
HINTERNET connection;
|
||
|
|
||
|
if (g_Dialed) {
|
||
|
pGoOffline();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we are online
|
||
|
//
|
||
|
|
||
|
connection = g_InternetOpenUrlA (
|
||
|
Session,
|
||
|
"http://www.microsoft.com/",
|
||
|
NULL,
|
||
|
0,
|
||
|
INTERNET_FLAG_RELOAD,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (connection) {
|
||
|
DEBUGMSG ((DBG_VERBOSE, "Able to connect to www.microsoft.com"));
|
||
|
g_InternetCloseHandle (connection);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unable to contact www.microsoft.com. Possibilities:
|
||
|
//
|
||
|
// - net cable unplugged
|
||
|
// - firewall without a proxy
|
||
|
// - no online connection (i.e., need to dial ISP)
|
||
|
// - www.microsoft.com or some part of the Internet is down
|
||
|
// - user has no Internet access at all
|
||
|
//
|
||
|
// Try RAS, then try connection again.
|
||
|
//
|
||
|
|
||
|
g_InternetDialA (NULL, NULL, INTERNET_AUTODIAL_FORCE_ONLINE, &g_Cxn, 0);
|
||
|
|
||
|
g_Dialed = TRUE;
|
||
|
|
||
|
connection = g_InternetOpenUrlA (
|
||
|
Session,
|
||
|
"http://www.microsoft.com/",
|
||
|
NULL,
|
||
|
0,
|
||
|
INTERNET_FLAG_RELOAD,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (connection) {
|
||
|
DEBUGMSG ((DBG_VERBOSE, "Able to connect to www.microsoft.com via RAS"));
|
||
|
g_InternetCloseHandle (connection);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
pGoOffline();
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
DownloadUpdates (
|
||
|
HANDLE CancelEvent, OPTIONAL
|
||
|
HANDLE WantToRetryEvent, OPTIONAL
|
||
|
HANDLE OkToRetryEvent, OPTIONAL
|
||
|
PCSTR *StatusUrl OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
HINTERNET session;
|
||
|
CHAR url[MAX_PATH];
|
||
|
DWORD size;
|
||
|
BOOL b = FALSE;
|
||
|
CHAR tempPath[MAX_TCHAR_PATH];
|
||
|
CHAR dirFile[MAX_TCHAR_PATH];
|
||
|
HINF inf;
|
||
|
INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
|
||
|
PCSTR p;
|
||
|
PCSTR q;
|
||
|
|
||
|
if (!pOpenWinInetSupport()) {
|
||
|
DEBUGMSG ((DBG_ERROR, "Can't open wininet.dll"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
|
||
|
session = g_InternetOpenA (
|
||
|
g_AppName,
|
||
|
INTERNET_OPEN_TYPE_PRECONFIG,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if (!session) {
|
||
|
DEBUGMSG ((DBG_ERROR, "InternetOpen returned NULL"));
|
||
|
SetLastError (ERROR_NOT_CONNECTED);
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (!pGoOnline (session)) {
|
||
|
DEBUGMSG ((DBG_ERROR, "Can't go online"));
|
||
|
SetLastError (ERROR_NOT_CONNECTED);
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
size = ARRAYSIZE(url);
|
||
|
|
||
|
if (!g_InternetCanonicalizeUrlA (g_DirFile, url, &size, 0)) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't canonicalize %s", g_DirFile));
|
||
|
SetLastError (ERROR_CONNECTION_ABORTED);
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
GetTempPathA (ARRAYSIZE(tempPath), tempPath);
|
||
|
GetTempFileNameA (tempPath, "ftp", 0, dirFile);
|
||
|
|
||
|
if (StatusUrl) {
|
||
|
*StatusUrl = url;
|
||
|
}
|
||
|
|
||
|
if (!pDownloadFileWithRetry (
|
||
|
session,
|
||
|
url,
|
||
|
dirFile,
|
||
|
CancelEvent,
|
||
|
WantToRetryEvent,
|
||
|
OkToRetryEvent
|
||
|
)) {
|
||
|
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't download %s", url));
|
||
|
|
||
|
// last error set to a valid return condition
|
||
|
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
inf = InfOpenInfFileA (dirFile);
|
||
|
|
||
|
if (inf == INVALID_HANDLE_VALUE) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't open %s", dirFile));
|
||
|
|
||
|
// last error set to the reason of the INF failure
|
||
|
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
__try {
|
||
|
if (InfFindFirstLineA (inf, "Win9xUpg", NULL, &is)) {
|
||
|
|
||
|
do {
|
||
|
|
||
|
p = InfGetStringFieldA (&is, 1);
|
||
|
q = InfGetStringFieldA (&is, 2);
|
||
|
|
||
|
if (!p || !q) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
q = ExpandEnvironmentTextA (q);
|
||
|
|
||
|
size = ARRAYSIZE(url);
|
||
|
|
||
|
if (!g_InternetCanonicalizeUrlA (p, url, &size, 0)) {
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't canonicalize INF-specified URL: %s", p));
|
||
|
SetLastError (ERROR_CONNECTION_ABORTED);
|
||
|
__leave;
|
||
|
}
|
||
|
|
||
|
if (!pDownloadFileWithRetry (
|
||
|
session,
|
||
|
url,
|
||
|
q,
|
||
|
CancelEvent,
|
||
|
WantToRetryEvent,
|
||
|
OkToRetryEvent
|
||
|
)) {
|
||
|
|
||
|
FreeTextA (q);
|
||
|
|
||
|
DEBUGMSGA ((DBG_ERROR, "Can't download INF-specified URL: %s", url));
|
||
|
|
||
|
// last error set to a valid return code
|
||
|
|
||
|
__leave;
|
||
|
|
||
|
}
|
||
|
|
||
|
FreeTextA (q);
|
||
|
|
||
|
} while (InfFindNextLine (&is));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
__finally {
|
||
|
InfCloseInfFile (inf);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
__finally {
|
||
|
if (session) {
|
||
|
g_InternetCloseHandle (session);
|
||
|
}
|
||
|
|
||
|
InfCleanUpInfStruct (&is);
|
||
|
DeleteFileA (dirFile);
|
||
|
|
||
|
pGoOffline();
|
||
|
|
||
|
pCloseWinInetSupport();
|
||
|
}
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
|