1495 lines
46 KiB
C
1495 lines
46 KiB
C
/*
|
|
* Process Hacker Plugins -
|
|
* Update Checker Plugin
|
|
*
|
|
* Copyright (C) 2011-2016 dmex
|
|
*
|
|
* This file is part of Process Hacker.
|
|
*
|
|
* Process Hacker is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Process Hacker is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "updater.h"
|
|
#include <shellapi.h>
|
|
#include <shlobj.h>
|
|
|
|
static HANDLE UpdateDialogThreadHandle = NULL;
|
|
static HWND UpdateDialogHandle = NULL;
|
|
static PH_EVENT InitializedEvent = PH_EVENT_INIT;
|
|
|
|
mxml_type_t QueryXmlDataCallback(
|
|
_In_ mxml_node_t *node
|
|
)
|
|
{
|
|
return MXML_OPAQUE;
|
|
}
|
|
|
|
BOOLEAN LastUpdateCheckExpired(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG64 lastUpdateTimeTicks = 0;
|
|
LARGE_INTEGER currentUpdateTimeTicks;
|
|
PPH_STRING lastUpdateTimeString;
|
|
|
|
// Get the last update check time
|
|
lastUpdateTimeString = PhGetStringSetting(SETTING_NAME_LAST_CHECK);
|
|
PhStringToInteger64(&lastUpdateTimeString->sr, 0, &lastUpdateTimeTicks);
|
|
|
|
// Query the current time
|
|
PhQuerySystemTime(¤tUpdateTimeTicks);
|
|
|
|
// Check if the last update check was more than 7 days ago
|
|
if (currentUpdateTimeTicks.QuadPart - lastUpdateTimeTicks >= 7 * PH_TICKS_PER_DAY)
|
|
{
|
|
PPH_STRING currentUpdateTimeString = PhFormatUInt64(currentUpdateTimeTicks.QuadPart, FALSE);
|
|
|
|
// Save the current time
|
|
PhSetStringSetting2(SETTING_NAME_LAST_CHECK, ¤tUpdateTimeString->sr);
|
|
|
|
// Cleanup
|
|
PhDereferenceObject(currentUpdateTimeString);
|
|
PhDereferenceObject(lastUpdateTimeString);
|
|
return TRUE;
|
|
}
|
|
|
|
// Cleanup
|
|
PhDereferenceObject(lastUpdateTimeString);
|
|
return FALSE;
|
|
}
|
|
|
|
PPH_STRING UpdateVersionString(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG majorVersion;
|
|
ULONG minorVersion;
|
|
ULONG revisionVersion;
|
|
PPH_STRING currentVersion = NULL;
|
|
PPH_STRING versionHeader = NULL;
|
|
|
|
PhGetPhVersionNumbers(
|
|
&majorVersion,
|
|
&minorVersion,
|
|
NULL,
|
|
&revisionVersion
|
|
);
|
|
|
|
currentVersion = PhFormatString(
|
|
L"%lu.%lu.%lu",
|
|
majorVersion,
|
|
minorVersion,
|
|
revisionVersion
|
|
);
|
|
|
|
if (currentVersion)
|
|
{
|
|
versionHeader = PhConcatStrings2(L"ProcessHacker-Build: ", currentVersion->Buffer);
|
|
PhDereferenceObject(currentVersion);
|
|
}
|
|
|
|
return versionHeader;
|
|
}
|
|
|
|
PPH_STRING UpdateWindowsString(
|
|
VOID
|
|
)
|
|
{
|
|
static PH_STRINGREF keyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows NT\\CurrentVersion");
|
|
|
|
HANDLE keyHandle = NULL;
|
|
PPH_STRING buildLabHeader = NULL;
|
|
|
|
if (NT_SUCCESS(PhOpenKey(
|
|
&keyHandle,
|
|
KEY_READ,
|
|
PH_KEY_LOCAL_MACHINE,
|
|
&keyName,
|
|
0
|
|
)))
|
|
{
|
|
PPH_STRING buildLabString;
|
|
|
|
if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLabEx"))
|
|
{
|
|
buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer);
|
|
PhDereferenceObject(buildLabString);
|
|
}
|
|
else if (buildLabString = PhQueryRegistryString(keyHandle, L"BuildLab"))
|
|
{
|
|
buildLabHeader = PhConcatStrings2(L"ProcessHacker-OsBuild: ", buildLabString->Buffer);
|
|
PhDereferenceObject(buildLabString);
|
|
}
|
|
|
|
NtClose(keyHandle);
|
|
}
|
|
|
|
return buildLabHeader;
|
|
}
|
|
|
|
BOOLEAN ParseVersionString(
|
|
_Inout_ PPH_UPDATER_CONTEXT Context
|
|
)
|
|
{
|
|
PH_STRINGREF sr, majorPart, minorPart, revisionPart;
|
|
ULONG64 majorInteger = 0, minorInteger = 0, revisionInteger = 0;
|
|
|
|
PhInitializeStringRef(&sr, Context->Version->Buffer);
|
|
PhInitializeStringRef(&revisionPart, Context->RevVersion->Buffer);
|
|
|
|
if (PhSplitStringRefAtChar(&sr, '.', &majorPart, &minorPart))
|
|
{
|
|
PhStringToInteger64(&majorPart, 10, &majorInteger);
|
|
PhStringToInteger64(&minorPart, 10, &minorInteger);
|
|
PhStringToInteger64(&revisionPart, 10, &revisionInteger);
|
|
|
|
Context->MajorVersion = (ULONG)majorInteger;
|
|
Context->MinorVersion = (ULONG)minorInteger;
|
|
Context->RevisionVersion = (ULONG)revisionInteger;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN ReadRequestString(
|
|
_In_ HINTERNET Handle,
|
|
_Out_ _Deref_post_z_cap_(*DataLength) PSTR *Data,
|
|
_Out_ ULONG *DataLength
|
|
)
|
|
{
|
|
PSTR data;
|
|
ULONG allocatedLength;
|
|
ULONG dataLength;
|
|
ULONG returnLength;
|
|
BYTE buffer[PAGE_SIZE];
|
|
|
|
allocatedLength = sizeof(buffer);
|
|
data = (PSTR)PhAllocate(allocatedLength);
|
|
dataLength = 0;
|
|
|
|
// Zero the buffer
|
|
memset(buffer, 0, PAGE_SIZE);
|
|
|
|
while (WinHttpReadData(Handle, buffer, PAGE_SIZE, &returnLength))
|
|
{
|
|
if (returnLength == 0)
|
|
break;
|
|
|
|
if (allocatedLength < dataLength + returnLength)
|
|
{
|
|
allocatedLength *= 2;
|
|
data = (PSTR)PhReAllocate(data, allocatedLength);
|
|
}
|
|
|
|
// Copy the returned buffer into our pointer
|
|
memcpy(data + dataLength, buffer, returnLength);
|
|
// Zero the returned buffer for the next loop
|
|
//memset(buffer, 0, returnLength);
|
|
|
|
dataLength += returnLength;
|
|
}
|
|
|
|
if (allocatedLength < dataLength + 1)
|
|
{
|
|
allocatedLength++;
|
|
data = (PSTR)PhReAllocate(data, allocatedLength);
|
|
}
|
|
|
|
// Ensure that the buffer is null-terminated.
|
|
data[dataLength] = 0;
|
|
|
|
*DataLength = dataLength;
|
|
*Data = data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
PPH_UPDATER_CONTEXT CreateUpdateContext(
|
|
VOID
|
|
)
|
|
{
|
|
PPH_UPDATER_CONTEXT context;
|
|
|
|
context = (PPH_UPDATER_CONTEXT)PhAllocate(sizeof(PH_UPDATER_CONTEXT));
|
|
memset(context, 0, sizeof(PH_UPDATER_CONTEXT));
|
|
|
|
return context;
|
|
}
|
|
|
|
VOID FreeUpdateContext(
|
|
_In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context
|
|
)
|
|
{
|
|
if (!Context)
|
|
return;
|
|
|
|
Context->HaveData = FALSE;
|
|
Context->UpdaterState = PhUpdateMaximum;
|
|
|
|
Context->MinorVersion = 0;
|
|
Context->MajorVersion = 0;
|
|
Context->RevisionVersion = 0;
|
|
Context->CurrentMinorVersion = 0;
|
|
Context->CurrentMajorVersion = 0;
|
|
Context->CurrentRevisionVersion = 0;
|
|
|
|
PhClearReference(&Context->Version);
|
|
PhClearReference(&Context->RevVersion);
|
|
PhClearReference(&Context->RelDate);
|
|
PhClearReference(&Context->Size);
|
|
PhClearReference(&Context->Hash);
|
|
PhClearReference(&Context->ReleaseNotesUrl);
|
|
PhClearReference(&Context->SetupFilePath);
|
|
PhClearReference(&Context->SetupFileDownloadUrl);
|
|
|
|
if (Context->FontHandle)
|
|
{
|
|
DeleteObject(Context->FontHandle);
|
|
Context->FontHandle = NULL;
|
|
}
|
|
|
|
if (Context->IconBitmap)
|
|
{
|
|
DeleteObject(Context->IconBitmap);
|
|
Context->IconBitmap = NULL;
|
|
}
|
|
|
|
if (Context->IconHandle)
|
|
{
|
|
DestroyIcon(Context->IconHandle);
|
|
Context->IconHandle = NULL;
|
|
}
|
|
|
|
PhFree(Context);
|
|
}
|
|
|
|
BOOLEAN QueryUpdateData(
|
|
_Inout_ PPH_UPDATER_CONTEXT Context,
|
|
_In_ BOOLEAN UseFailServer
|
|
)
|
|
{
|
|
BOOLEAN isSuccess = FALSE;
|
|
HINTERNET httpSessionHandle = NULL;
|
|
HINTERNET httpConnectionHandle = NULL;
|
|
HINTERNET httpRequestHandle = NULL;
|
|
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 };
|
|
mxml_node_t* xmlNode = NULL;
|
|
ULONG xmlStringBufferLength = 0;
|
|
PSTR xmlStringBuffer = NULL;
|
|
PPH_STRING versionHeader = UpdateVersionString();
|
|
PPH_STRING windowsHeader = UpdateWindowsString();
|
|
|
|
// Get the current Process Hacker version
|
|
PhGetPhVersionNumbers(
|
|
&Context->CurrentMajorVersion,
|
|
&Context->CurrentMinorVersion,
|
|
NULL,
|
|
&Context->CurrentRevisionVersion
|
|
);
|
|
|
|
__try
|
|
{
|
|
// Query the current system proxy
|
|
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
|
|
|
|
// Open the HTTP session with the system proxy configuration if available
|
|
if (!(httpSessionHandle = WinHttpOpen(
|
|
NULL,
|
|
proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
|
proxyConfig.lpszProxy,
|
|
proxyConfig.lpszProxyBypass,
|
|
0
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (WindowsVersion >= WINDOWS_8_1)
|
|
{
|
|
// Enable GZIP and DEFLATE support on Windows 8.1 and above using undocumented flags.
|
|
ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE;
|
|
|
|
WinHttpSetOption(
|
|
httpSessionHandle,
|
|
WINHTTP_OPTION_DECOMPRESSION,
|
|
&httpFlags,
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
if (UseFailServer)
|
|
{
|
|
if (!(httpConnectionHandle = WinHttpConnect(
|
|
httpSessionHandle,
|
|
L"processhacker.sourceforge.net",
|
|
INTERNET_DEFAULT_HTTP_PORT,
|
|
0
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (!(httpRequestHandle = WinHttpOpenRequest(
|
|
httpConnectionHandle,
|
|
NULL,
|
|
L"/update.php",
|
|
NULL,
|
|
WINHTTP_NO_REFERER,
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
WINHTTP_FLAG_REFRESH
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!(httpConnectionHandle = WinHttpConnect(
|
|
httpSessionHandle,
|
|
L"wj32.org",
|
|
INTERNET_DEFAULT_HTTPS_PORT,
|
|
0
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (!(httpRequestHandle = WinHttpOpenRequest(
|
|
httpConnectionHandle,
|
|
NULL,
|
|
L"/processhacker/update.php",
|
|
NULL,
|
|
WINHTTP_NO_REFERER,
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
WINHTTP_FLAG_REFRESH | WINHTTP_FLAG_SECURE
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
if (versionHeader)
|
|
{
|
|
WinHttpAddRequestHeaders(
|
|
httpRequestHandle,
|
|
versionHeader->Buffer,
|
|
(ULONG)versionHeader->Length / sizeof(WCHAR),
|
|
WINHTTP_ADDREQ_FLAG_ADD
|
|
);
|
|
}
|
|
|
|
if (windowsHeader)
|
|
{
|
|
WinHttpAddRequestHeaders(
|
|
httpRequestHandle,
|
|
windowsHeader->Buffer,
|
|
(ULONG)windowsHeader->Length / sizeof(WCHAR),
|
|
WINHTTP_ADDREQ_FLAG_ADD
|
|
);
|
|
}
|
|
|
|
if (!WinHttpSendRequest(
|
|
httpRequestHandle,
|
|
WINHTTP_NO_ADDITIONAL_HEADERS,
|
|
0,
|
|
WINHTTP_NO_REQUEST_DATA,
|
|
0,
|
|
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
|
|
0
|
|
))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (!WinHttpReceiveResponse(httpRequestHandle, NULL))
|
|
__leave;
|
|
|
|
// Read the resulting xml into our buffer.
|
|
if (!ReadRequestString(httpRequestHandle, &xmlStringBuffer, &xmlStringBufferLength))
|
|
__leave;
|
|
|
|
// Check the buffer for valid data.
|
|
if (xmlStringBuffer == NULL || xmlStringBuffer[0] == '\0')
|
|
__leave;
|
|
|
|
// Load our XML
|
|
xmlNode = mxmlLoadString(NULL, xmlStringBuffer, QueryXmlDataCallback);
|
|
if (xmlNode == NULL || xmlNode->type != MXML_ELEMENT)
|
|
__leave;
|
|
|
|
// Find the version node
|
|
Context->Version = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "ver", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->Version))
|
|
__leave;
|
|
|
|
// Find the revision node
|
|
Context->RevVersion = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "rev", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->RevVersion))
|
|
__leave;
|
|
|
|
// Find the release date node
|
|
Context->RelDate = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "reldate", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->RelDate))
|
|
__leave;
|
|
|
|
// Find the size node
|
|
Context->Size = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "size", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->Size))
|
|
__leave;
|
|
|
|
//Find the hash node
|
|
Context->Hash = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "sha1", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->Hash))
|
|
__leave;
|
|
|
|
// Find the release notes URL
|
|
Context->ReleaseNotesUrl = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "relnotes", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->ReleaseNotesUrl))
|
|
__leave;
|
|
|
|
// Find the installer download URL
|
|
Context->SetupFileDownloadUrl = PhGetOpaqueXmlNodeText(
|
|
mxmlFindElement(xmlNode->child, xmlNode, "setupurl", NULL, NULL, MXML_DESCEND)
|
|
);
|
|
if (PhIsNullOrEmptyString(Context->SetupFileDownloadUrl))
|
|
__leave;
|
|
|
|
if (!ParseVersionString(Context))
|
|
__leave;
|
|
|
|
isSuccess = TRUE;
|
|
}
|
|
__finally
|
|
{
|
|
if (httpRequestHandle)
|
|
WinHttpCloseHandle(httpRequestHandle);
|
|
|
|
if (httpConnectionHandle)
|
|
WinHttpCloseHandle(httpConnectionHandle);
|
|
|
|
if (httpSessionHandle)
|
|
WinHttpCloseHandle(httpSessionHandle);
|
|
|
|
if (xmlNode)
|
|
mxmlDelete(xmlNode);
|
|
|
|
if (xmlStringBuffer)
|
|
PhFree(xmlStringBuffer);
|
|
|
|
PhClearReference(&versionHeader);
|
|
PhClearReference(&windowsHeader);
|
|
}
|
|
|
|
return isSuccess;
|
|
}
|
|
|
|
NTSTATUS UpdateCheckSilentThread(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PPH_UPDATER_CONTEXT context = NULL;
|
|
ULONGLONG currentVersion = 0;
|
|
ULONGLONG latestVersion = 0;
|
|
|
|
context = CreateUpdateContext();
|
|
|
|
__try
|
|
{
|
|
if (!LastUpdateCheckExpired())
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (!QueryUpdateData(context, FALSE))
|
|
{
|
|
if (!QueryUpdateData(context, TRUE))
|
|
{
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
currentVersion = MAKEDLLVERULL(
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
0,
|
|
context->CurrentRevisionVersion
|
|
);
|
|
|
|
#ifdef DEBUG_UPDATE
|
|
latestVersion = MAKEDLLVERULL(
|
|
9999,
|
|
9999,
|
|
0,
|
|
9999
|
|
);
|
|
#else
|
|
latestVersion = MAKEDLLVERULL(
|
|
context->MajorVersion,
|
|
context->MinorVersion,
|
|
0,
|
|
context->RevisionVersion
|
|
);
|
|
#endif
|
|
|
|
if (WindowsVersion < WINDOWS_7 && context->MajorVersion != 2)
|
|
{
|
|
// If we're running on XP or Vista, we only support 2.x builds and below.
|
|
latestVersion = currentVersion;
|
|
}
|
|
|
|
// Compare the current version against the latest available version
|
|
if (currentVersion < latestVersion)
|
|
{
|
|
// Don't spam the user the second they open PH, delay dialog creation for 3 seconds.
|
|
Sleep(3000);
|
|
|
|
if (!UpdateDialogHandle)
|
|
{
|
|
// We have data we're going to cache and pass into the dialog
|
|
context->HaveData = TRUE;
|
|
|
|
// Show the dialog asynchronously on a new thread.
|
|
ShowUpdateDialog(context);
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
// Check the dialog doesn't own the window context...
|
|
if (!context->HaveData)
|
|
{
|
|
FreeUpdateContext(context);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS UpdateCheckThread(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PPH_UPDATER_CONTEXT context = NULL;
|
|
ULONGLONG currentVersion = 0;
|
|
ULONGLONG latestVersion = 0;
|
|
|
|
context = (PPH_UPDATER_CONTEXT)Parameter;
|
|
|
|
// Check if we have cached update data
|
|
if (!context->HaveData)
|
|
{
|
|
context->HaveData = QueryUpdateData(context, FALSE);
|
|
|
|
if (!context->HaveData)
|
|
{
|
|
context->HaveData = QueryUpdateData(context, TRUE);
|
|
}
|
|
}
|
|
|
|
// sanity check
|
|
if (!context->HaveData)
|
|
{
|
|
PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
currentVersion = MAKEDLLVERULL(
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
0,
|
|
context->CurrentRevisionVersion
|
|
);
|
|
|
|
#ifdef DEBUG_UPDATE
|
|
latestVersion = MAKEDLLVERULL(
|
|
9999,
|
|
9999,
|
|
0,
|
|
9999
|
|
);
|
|
#else
|
|
latestVersion = MAKEDLLVERULL(
|
|
context->MajorVersion,
|
|
context->MinorVersion,
|
|
0,
|
|
context->RevisionVersion
|
|
);
|
|
#endif
|
|
|
|
if (WindowsVersion < WINDOWS_7 && context->MajorVersion != 2)
|
|
{
|
|
// If we're running on XP or Vista, we only support 2.x builds and below.
|
|
PostMessage(context->DialogHandle, PH_UPDATENOTSUPPORTED, 0, 0);
|
|
}
|
|
else if (currentVersion == latestVersion)
|
|
{
|
|
// User is running the latest version
|
|
PostMessage(context->DialogHandle, PH_UPDATEISCURRENT, 0, 0);
|
|
}
|
|
else if (currentVersion > latestVersion)
|
|
{
|
|
// User is running a newer version
|
|
PostMessage(context->DialogHandle, PH_UPDATENEWER, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// User is running an older version
|
|
PostMessage(context->DialogHandle, PH_UPDATEAVAILABLE, 0, 0);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS UpdateDownloadThread(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
BOOLEAN downloadSuccess = FALSE;
|
|
BOOLEAN hashSuccess = FALSE;
|
|
BOOLEAN verifySuccess = FALSE;
|
|
HANDLE tempFileHandle = NULL;
|
|
HINTERNET httpSessionHandle = NULL;
|
|
HINTERNET httpConnectionHandle = NULL;
|
|
HINTERNET httpRequestHandle = NULL;
|
|
PPH_STRING setupTempPath = NULL;
|
|
PPH_STRING downloadHostPath = NULL;
|
|
PPH_STRING downloadUrlPath = NULL;
|
|
PPH_STRING userAgentString = NULL;
|
|
PPH_STRING fullSetupPath = NULL;
|
|
PPH_STRING randomGuidString = NULL;
|
|
ULONG indexOfFileName = -1;
|
|
GUID randomGuid;
|
|
URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) };
|
|
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 };
|
|
|
|
PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter;
|
|
|
|
__try
|
|
{
|
|
// Create a user agent string.
|
|
userAgentString = PhFormatString(
|
|
L"PH_%lu.%lu_%lu",
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
context->CurrentRevisionVersion
|
|
);
|
|
if (PhIsNullOrEmptyString(userAgentString))
|
|
__leave;
|
|
|
|
// Allocate the GetTempPath buffer
|
|
setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR));
|
|
if (PhIsNullOrEmptyString(setupTempPath))
|
|
__leave;
|
|
|
|
// Get the temp path
|
|
if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0)
|
|
__leave;
|
|
if (PhIsNullOrEmptyString(setupTempPath))
|
|
__leave;
|
|
|
|
// Generate random guid for our directory path.
|
|
PhGenerateGuid(&randomGuid);
|
|
|
|
if (randomGuidString = PhFormatGuid(&randomGuid))
|
|
{
|
|
PPH_STRING guidSubString;
|
|
|
|
// Strip the left and right curly brackets.
|
|
guidSubString = PhSubstring(randomGuidString, 1, randomGuidString->Length / sizeof(WCHAR) - 2);
|
|
|
|
PhSwapReference(&randomGuidString, guidSubString);
|
|
}
|
|
|
|
// Append the tempath to our string: %TEMP%RandomString\\processhacker-%lu.%lu-setup.exe
|
|
// Example: C:\\Users\\dmex\\AppData\\Temp\\ABCD\\processhacker-2.90-setup.exe
|
|
context->SetupFilePath = PhFormatString(
|
|
L"%s%s\\processhacker-%lu.%lu-setup.exe",
|
|
setupTempPath->Buffer,
|
|
randomGuidString->Buffer,
|
|
context->MajorVersion,
|
|
context->MinorVersion
|
|
);
|
|
if (PhIsNullOrEmptyString(context->SetupFilePath))
|
|
__leave;
|
|
|
|
// Create the directory if it does not exist.
|
|
if (fullSetupPath = PhGetFullPath(context->SetupFilePath->Buffer, &indexOfFileName))
|
|
{
|
|
PPH_STRING directoryPath;
|
|
|
|
if (indexOfFileName == -1)
|
|
__leave;
|
|
|
|
if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName))
|
|
{
|
|
SHCreateDirectoryEx(NULL, directoryPath->Buffer, NULL);
|
|
PhDereferenceObject(directoryPath);
|
|
}
|
|
}
|
|
|
|
// Create output file
|
|
if (!NT_SUCCESS(PhCreateFileWin32(
|
|
&tempFileHandle,
|
|
context->SetupFilePath->Buffer,
|
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_TEMPORARY,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
// Set lengths to non-zero enabling these params to be cracked.
|
|
httpUrlComponents.dwSchemeLength = (ULONG)-1;
|
|
httpUrlComponents.dwHostNameLength = (ULONG)-1;
|
|
httpUrlComponents.dwUrlPathLength = (ULONG)-1;
|
|
|
|
if (!WinHttpCrackUrl(
|
|
context->SetupFileDownloadUrl->Buffer,
|
|
(ULONG)context->SetupFileDownloadUrl->Length,
|
|
0,
|
|
&httpUrlComponents
|
|
))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
// Create the Host string.
|
|
downloadHostPath = PhCreateStringEx(
|
|
httpUrlComponents.lpszHostName,
|
|
httpUrlComponents.dwHostNameLength * sizeof(WCHAR)
|
|
);
|
|
if (PhIsNullOrEmptyString(downloadHostPath))
|
|
__leave;
|
|
|
|
// Create the Path string.
|
|
downloadUrlPath = PhCreateStringEx(
|
|
httpUrlComponents.lpszUrlPath,
|
|
httpUrlComponents.dwUrlPathLength * sizeof(WCHAR)
|
|
);
|
|
if (PhIsNullOrEmptyString(downloadUrlPath))
|
|
__leave;
|
|
|
|
SetDlgItemText(context->DialogHandle, IDC_STATUS, L"Connecting...");
|
|
|
|
// Query the current system proxy
|
|
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
|
|
|
|
// Open the HTTP session with the system proxy configuration if available
|
|
if (!(httpSessionHandle = WinHttpOpen(
|
|
userAgentString->Buffer,
|
|
proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
|
|
proxyConfig.lpszProxy,
|
|
proxyConfig.lpszProxyBypass,
|
|
0
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (WindowsVersion >= WINDOWS_8_1)
|
|
{
|
|
// Enable GZIP and DEFLATE support on Windows 8.1 and above using undocumented flags.
|
|
ULONG httpFlags = WINHTTP_DECOMPRESSION_FLAG_GZIP | WINHTTP_DECOMPRESSION_FLAG_DEFLATE;
|
|
|
|
WinHttpSetOption(
|
|
httpSessionHandle,
|
|
WINHTTP_OPTION_DECOMPRESSION,
|
|
&httpFlags,
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
if (!(httpConnectionHandle = WinHttpConnect(
|
|
httpSessionHandle,
|
|
downloadHostPath->Buffer,
|
|
httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT,
|
|
0
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
if (!(httpRequestHandle = WinHttpOpenRequest(
|
|
httpConnectionHandle,
|
|
NULL,
|
|
downloadUrlPath->Buffer,
|
|
NULL,
|
|
WINHTTP_NO_REFERER,
|
|
WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0)
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
SetDlgItemText(context->DialogHandle, IDC_STATUS, L"Sending request...");
|
|
|
|
if (!WinHttpSendRequest(
|
|
httpRequestHandle,
|
|
WINHTTP_NO_ADDITIONAL_HEADERS,
|
|
0,
|
|
WINHTTP_NO_REQUEST_DATA,
|
|
0,
|
|
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
|
|
0
|
|
))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
SetDlgItemText(context->DialogHandle, IDC_STATUS, L"Waiting for response...");
|
|
|
|
if (WinHttpReceiveResponse(httpRequestHandle, NULL))
|
|
{
|
|
ULONG bytesDownloaded = 0;
|
|
ULONG downloadedBytes = 0;
|
|
ULONG contentLengthSize = sizeof(ULONG);
|
|
ULONG contentLength = 0;
|
|
BYTE buffer[PAGE_SIZE];
|
|
BYTE hashBuffer[20];
|
|
|
|
PH_HASH_CONTEXT hashContext;
|
|
IO_STATUS_BLOCK isb;
|
|
|
|
if (!WinHttpQueryHeaders(
|
|
httpRequestHandle,
|
|
WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
|
|
WINHTTP_HEADER_NAME_BY_INDEX,
|
|
&contentLength,
|
|
&contentLengthSize,
|
|
0
|
|
))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
// Initialize hash algorithm.
|
|
PhInitializeHash(&hashContext, Sha1HashAlgorithm);
|
|
|
|
// Zero the buffer.
|
|
memset(buffer, 0, PAGE_SIZE);
|
|
|
|
// Download the data.
|
|
while (WinHttpReadData(httpRequestHandle, buffer, PAGE_SIZE, &bytesDownloaded))
|
|
{
|
|
// If we get zero bytes, the file was uploaded or there was an error
|
|
if (bytesDownloaded == 0)
|
|
break;
|
|
|
|
// If the dialog was closed, just cleanup and exit
|
|
//if (context->UpdaterState == PhUpdateMaximum)
|
|
if (!UpdateDialogThreadHandle)
|
|
__leave;
|
|
|
|
// Update the hash of bytes we downloaded.
|
|
PhUpdateHash(&hashContext, buffer, bytesDownloaded);
|
|
|
|
// Write the downloaded bytes to disk.
|
|
if (!NT_SUCCESS(NtWriteFile(
|
|
tempFileHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&isb,
|
|
buffer,
|
|
bytesDownloaded,
|
|
NULL,
|
|
NULL
|
|
)))
|
|
{
|
|
__leave;
|
|
}
|
|
|
|
downloadedBytes += (DWORD)isb.Information;
|
|
|
|
// Check the number of bytes written are the same we downloaded.
|
|
if (bytesDownloaded != isb.Information)
|
|
__leave;
|
|
|
|
// Update the GUI progress.
|
|
// TODO: Update on GUI thread.
|
|
{
|
|
//int percent = MulDiv(100, downloadedBytes, contentLength);
|
|
FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100);
|
|
PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1);
|
|
PPH_STRING totalLength = PhFormatSize(contentLength, -1);
|
|
|
|
PPH_STRING dlLengthString = PhFormatString(
|
|
L"%s of %s (%.0f%%)",
|
|
totalDownloaded->Buffer,
|
|
totalLength->Buffer,
|
|
percent
|
|
);
|
|
|
|
// Update the progress bar position
|
|
SendMessage(context->ProgressHandle, PBM_SETPOS, (ULONG)percent, 0);
|
|
Static_SetText(context->StatusHandle, dlLengthString->Buffer);
|
|
|
|
PhDereferenceObject(dlLengthString);
|
|
PhDereferenceObject(totalDownloaded);
|
|
PhDereferenceObject(totalLength);
|
|
}
|
|
}
|
|
|
|
// Compute hash result (will fail if file not downloaded correctly).
|
|
if (PhFinalHash(&hashContext, &hashBuffer, 20, NULL))
|
|
{
|
|
// Allocate our hash string, hex the final hash result in our hashBuffer.
|
|
PPH_STRING hexString = PhBufferToHexString(hashBuffer, 20);
|
|
|
|
if (PhEqualString(hexString, context->Hash, TRUE))
|
|
{
|
|
hashSuccess = TRUE;
|
|
}
|
|
|
|
PhDereferenceObject(hexString);
|
|
}
|
|
}
|
|
|
|
downloadSuccess = TRUE;
|
|
}
|
|
__finally
|
|
{
|
|
if (tempFileHandle)
|
|
NtClose(tempFileHandle);
|
|
|
|
if (httpRequestHandle)
|
|
WinHttpCloseHandle(httpRequestHandle);
|
|
|
|
if (httpConnectionHandle)
|
|
WinHttpCloseHandle(httpConnectionHandle);
|
|
|
|
if (httpSessionHandle)
|
|
WinHttpCloseHandle(httpSessionHandle);
|
|
|
|
PhClearReference(&randomGuidString);
|
|
PhClearReference(&fullSetupPath);
|
|
PhClearReference(&setupTempPath);
|
|
PhClearReference(&downloadHostPath);
|
|
PhClearReference(&downloadUrlPath);
|
|
PhClearReference(&userAgentString);
|
|
}
|
|
|
|
if (WindowsVersion < WINDOWS_8)
|
|
{
|
|
// Disable signature checking on XP, Vista and Win7 due to SHA2 certificate issues.
|
|
verifySuccess = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Check the digital signature of the installer...
|
|
if (context->SetupFilePath && PhVerifyFile(context->SetupFilePath->Buffer, NULL) == VrTrusted)
|
|
{
|
|
verifySuccess = TRUE;
|
|
}
|
|
}
|
|
|
|
if (downloadSuccess && hashSuccess && verifySuccess)
|
|
{
|
|
PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0);
|
|
}
|
|
else if (downloadSuccess)
|
|
{
|
|
PostMessage(context->DialogHandle, PH_UPDATEFAILURE, verifySuccess, hashSuccess);
|
|
}
|
|
else
|
|
{
|
|
PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
INT_PTR CALLBACK UpdaterWndProc(
|
|
_In_ HWND hwndDlg,
|
|
_In_ UINT uMsg,
|
|
_In_ WPARAM wParam,
|
|
_In_ LPARAM lParam
|
|
)
|
|
{
|
|
PPH_UPDATER_CONTEXT context = NULL;
|
|
|
|
if (uMsg == WM_INITDIALOG)
|
|
{
|
|
context = (PPH_UPDATER_CONTEXT)lParam;
|
|
SetProp(hwndDlg, L"Context", (HANDLE)context);
|
|
}
|
|
else
|
|
{
|
|
context = (PPH_UPDATER_CONTEXT)GetProp(hwndDlg, L"Context");
|
|
|
|
if (uMsg == WM_NCDESTROY)
|
|
{
|
|
RemoveProp(hwndDlg, L"Context");
|
|
FreeUpdateContext(context);
|
|
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
|
|
if (context == NULL)
|
|
return FALSE;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
LOGFONT logFont;
|
|
HWND parentWindow = GetParent(hwndDlg);
|
|
|
|
context->DialogHandle = hwndDlg;
|
|
context->StatusHandle = GetDlgItem(hwndDlg, IDC_STATUS);
|
|
context->ProgressHandle = GetDlgItem(hwndDlg, IDC_PROGRESS);
|
|
|
|
if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0))
|
|
{
|
|
HDC hdc;
|
|
|
|
if (hdc = GetDC(hwndDlg))
|
|
{
|
|
// Create the font handle
|
|
context->FontHandle = CreateFont(
|
|
-MulDiv(-14, GetDeviceCaps(hdc, LOGPIXELSY), 72),
|
|
0,
|
|
0,
|
|
0,
|
|
FW_MEDIUM,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE,
|
|
ANSI_CHARSET,
|
|
OUT_DEFAULT_PRECIS,
|
|
CLIP_DEFAULT_PRECIS,
|
|
CLEARTYPE_QUALITY | ANTIALIASED_QUALITY,
|
|
DEFAULT_PITCH,
|
|
logFont.lfFaceName
|
|
);
|
|
|
|
ReleaseDC(hwndDlg, hdc);
|
|
}
|
|
}
|
|
|
|
// Load the Process Hacker icon.
|
|
context->IconHandle = (HICON)LoadImage(
|
|
NtCurrentPeb()->ImageBaseAddress,
|
|
MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER),
|
|
IMAGE_ICON,
|
|
GetSystemMetrics(SM_CXICON),
|
|
GetSystemMetrics(SM_CYICON),
|
|
0
|
|
);
|
|
|
|
context->IconBitmap = PhIconToBitmap(context->IconHandle, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
|
|
|
|
// Set the window icons
|
|
if (context->IconHandle)
|
|
SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)context->IconHandle);
|
|
// Set the text font
|
|
if (context->FontHandle)
|
|
SetWindowFont(GetDlgItem(hwndDlg, IDC_MESSAGE), context->FontHandle, FALSE);
|
|
// Set the window image
|
|
if (context->IconBitmap)
|
|
SendMessage(GetDlgItem(hwndDlg, IDC_UPDATEICON), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)context->IconBitmap);
|
|
|
|
// Center the update window on PH if it's visible else we center on the desktop.
|
|
PhCenterWindow(hwndDlg, (IsWindowVisible(parentWindow) && !IsMinimized(parentWindow)) ? parentWindow : NULL);
|
|
|
|
// Show new version info (from the background update check)
|
|
if (context->HaveData)
|
|
{
|
|
HANDLE updateCheckThread = NULL;
|
|
|
|
// Create the update check thread.
|
|
if (updateCheckThread = PhCreateThread(0, UpdateCheckThread, context))
|
|
NtClose(updateCheckThread);
|
|
}
|
|
}
|
|
break;
|
|
case WM_SHOWDIALOG:
|
|
{
|
|
if (IsMinimized(hwndDlg))
|
|
ShowWindow(hwndDlg, SW_RESTORE);
|
|
else
|
|
ShowWindow(hwndDlg, SW_SHOW);
|
|
|
|
SetForegroundWindow(hwndDlg);
|
|
}
|
|
break;
|
|
case WM_CTLCOLORBTN:
|
|
case WM_CTLCOLORDLG:
|
|
case WM_CTLCOLORSTATIC:
|
|
{
|
|
HDC hDC = (HDC)wParam;
|
|
HWND hwndChild = (HWND)lParam;
|
|
|
|
// Check for our static label and change the color.
|
|
if (GetWindowID(hwndChild) == IDC_MESSAGE)
|
|
{
|
|
SetTextColor(hDC, RGB(19, 112, 171));
|
|
}
|
|
|
|
// Set a transparent background for the control backcolor.
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
|
|
// set window background color.
|
|
return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
{
|
|
switch (GET_WM_COMMAND_ID(wParam, lParam))
|
|
{
|
|
case IDCANCEL:
|
|
case IDOK:
|
|
DestroyWindow(hwndDlg);
|
|
break;
|
|
case IDC_DOWNLOAD:
|
|
{
|
|
switch (context->UpdaterState)
|
|
{
|
|
case PhUpdateDefault:
|
|
{
|
|
HANDLE updateCheckThread = NULL;
|
|
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, L"Checking for new releases...");
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, L"");
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
|
|
|
|
if (updateCheckThread = PhCreateThread(0, UpdateCheckThread, context))
|
|
NtClose(updateCheckThread);
|
|
}
|
|
break;
|
|
case PhUpdateDownload:
|
|
{
|
|
// We can't download updates below Vista due to SHA2 and the
|
|
// WINHTTP_OPTION_SECURITY_FLAGS option doesn't work on XP for some reason.
|
|
// Just show the downloads page when there is a new release.
|
|
if (WindowsVersion >= WINDOWS_VISTA && PhInstalledUsingSetup())
|
|
{
|
|
HANDLE downloadThreadHandle = NULL;
|
|
|
|
// Disable the download button
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
|
|
|
|
// Reset the progress bar (might be a download retry)
|
|
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETPOS, 0, 0);
|
|
|
|
if (WindowsVersion >= WINDOWS_VISTA)
|
|
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETSTATE, PBST_NORMAL, 0);
|
|
|
|
// Start file download thread
|
|
if (downloadThreadHandle = PhCreateThread(0, (PUSER_THREAD_START_ROUTINE)UpdateDownloadThread, context))
|
|
NtClose(downloadThreadHandle);
|
|
}
|
|
else
|
|
{
|
|
// Let the user handle non-setup installation, show the homepage and close this dialog.
|
|
PhShellExecute(hwndDlg, L"http://processhacker.sourceforge.net/downloads.php", NULL);
|
|
DestroyWindow(hwndDlg);
|
|
}
|
|
}
|
|
break;
|
|
case PhUpdateInstall:
|
|
{
|
|
SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) };
|
|
|
|
if (PhIsNullOrEmptyString(context->SetupFilePath))
|
|
break;
|
|
|
|
info.lpFile = context->SetupFilePath->Buffer;
|
|
info.lpVerb = PhGetOwnTokenAttributes().Elevated ? NULL : L"runas";
|
|
info.nShow = SW_SHOW;
|
|
info.hwnd = hwndDlg;
|
|
|
|
ProcessHacker_PrepareForEarlyShutdown(PhMainWndHandle);
|
|
|
|
if (!ShellExecuteEx(&info))
|
|
{
|
|
// Install failed, cancel the shutdown.
|
|
ProcessHacker_CancelEarlyShutdown(PhMainWndHandle);
|
|
|
|
// Set button text for next action
|
|
Button_SetText(GetDlgItem(hwndDlg, IDC_DOWNLOAD), L"Retry");
|
|
}
|
|
else
|
|
{
|
|
ProcessHacker_Destroy(PhMainWndHandle);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case PH_UPDATEAVAILABLE:
|
|
{
|
|
// Set updater state
|
|
context->UpdaterState = PhUpdateDownload;
|
|
|
|
// Set the UI text
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, PhaFormatString(
|
|
L"Process Hacker %lu.%lu.%lu",
|
|
context->MajorVersion,
|
|
context->MinorVersion,
|
|
context->RevisionVersion
|
|
)->Buffer);
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, PhaFormatString(
|
|
L"Released: %s",
|
|
context->RelDate->Buffer
|
|
)->Buffer);
|
|
SetDlgItemText(hwndDlg, IDC_STATUS, PhaFormatString(
|
|
L"Size: %s",
|
|
context->Size->Buffer
|
|
)->Buffer);
|
|
Button_SetText(GetDlgItem(hwndDlg, IDC_DOWNLOAD), L"Download");
|
|
|
|
// Enable the controls
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), TRUE);
|
|
Control_Visible(GetDlgItem(hwndDlg, IDC_PROGRESS), TRUE);
|
|
Control_Visible(GetDlgItem(hwndDlg, IDC_INFOSYSLINK), TRUE);
|
|
}
|
|
break;
|
|
case PH_UPDATEISCURRENT:
|
|
{
|
|
// Set updater state
|
|
context->UpdaterState = PhUpdateMaximum;
|
|
|
|
// Set the UI text
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, L"You're running the latest version.");
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, PhaFormatString(
|
|
L"Stable release build: v%lu.%lu.%lu",
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
context->CurrentRevisionVersion
|
|
)->Buffer);
|
|
|
|
// Disable the download button
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
|
|
// Enable the changelog link
|
|
Control_Visible(GetDlgItem(hwndDlg, IDC_INFOSYSLINK), TRUE);
|
|
}
|
|
break;
|
|
case PH_UPDATENEWER:
|
|
{
|
|
context->UpdaterState = PhUpdateMaximum;
|
|
|
|
// Set the UI text
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, L"You're running a newer version!");
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, PhaFormatString(
|
|
L"Pre-release build: v%lu.%lu.%lu",
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
context->CurrentRevisionVersion
|
|
)->Buffer);
|
|
|
|
// Disable the download button
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
|
|
// Disable the changelog link
|
|
Control_Visible(GetDlgItem(hwndDlg, IDC_INFOSYSLINK), FALSE);
|
|
}
|
|
break;
|
|
case PH_UPDATESUCCESS:
|
|
{
|
|
context->UpdaterState = PhUpdateInstall;
|
|
|
|
// If PH is not elevated, set the UAC shield for the install button as the setup requires elevation.
|
|
if (!PhGetOwnTokenAttributes().Elevated)
|
|
SendMessage(GetDlgItem(hwndDlg, IDC_DOWNLOAD), BCM_SETSHIELD, 0, TRUE);
|
|
|
|
// Set the download result, don't include hash status since it succeeded.
|
|
SetDlgItemText(hwndDlg, IDC_STATUS, L"Click Install to continue update...");
|
|
|
|
// Set button text for next action
|
|
Button_SetText(GetDlgItem(hwndDlg, IDC_DOWNLOAD), L"Install");
|
|
// Enable the Download/Install button so the user can install the update
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), TRUE);
|
|
}
|
|
break;
|
|
case PH_UPDATEFAILURE:
|
|
{
|
|
context->UpdaterState = PhUpdateDefault;
|
|
|
|
if (WindowsVersion >= WINDOWS_VISTA)
|
|
SendDlgItemMessage(hwndDlg, IDC_PROGRESS, PBM_SETSTATE, PBST_ERROR, 0);
|
|
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, L"Please check for updates again...");
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, L"An error was encountered while checking for updates.");
|
|
|
|
if ((BOOLEAN)wParam)
|
|
SetDlgItemText(hwndDlg, IDC_STATUS, L"Hash check failed.");
|
|
else if ((BOOLEAN)lParam)
|
|
SetDlgItemText(hwndDlg, IDC_STATUS, L"Signature check failed.");
|
|
|
|
// Set button text for next action
|
|
Button_SetText(GetDlgItem(hwndDlg, IDC_DOWNLOAD), L"Retry");
|
|
// Enable the Install button
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), TRUE);
|
|
// Hash failed, reset state to downloading so user can redownload the file.
|
|
}
|
|
break;
|
|
case PH_UPDATEISERRORED:
|
|
{
|
|
context->UpdaterState = PhUpdateDefault;
|
|
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, L"Please check for updates again...");
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, L"An error was encountered while checking for updates.");
|
|
|
|
Button_SetText(GetDlgItem(hwndDlg, IDC_DOWNLOAD), L"Retry");
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), TRUE);
|
|
}
|
|
break;
|
|
case PH_UPDATENOTSUPPORTED:
|
|
{
|
|
// Set updater state
|
|
context->UpdaterState = PhUpdateMaximum;
|
|
|
|
// Set the UI text
|
|
SetDlgItemText(hwndDlg, IDC_MESSAGE, PhaFormatString(
|
|
L"You're running the latest version: v%lu.%lu.%lu",
|
|
context->CurrentMajorVersion,
|
|
context->CurrentMinorVersion,
|
|
context->CurrentRevisionVersion
|
|
)->Buffer);
|
|
SetDlgItemText(hwndDlg, IDC_RELDATE, PhaFormatString(
|
|
L"v%lu.%lu.%lu is available for Windows 7 and above.",
|
|
context->MajorVersion,
|
|
context->MinorVersion,
|
|
context->RevisionVersion
|
|
)->Buffer);
|
|
|
|
// Disable the download button
|
|
Button_Enable(GetDlgItem(hwndDlg, IDC_DOWNLOAD), FALSE);
|
|
// Enable the changelog link
|
|
Control_Visible(GetDlgItem(hwndDlg, IDC_INFOSYSLINK), TRUE);
|
|
}
|
|
break;
|
|
case WM_NOTIFY:
|
|
{
|
|
switch (((LPNMHDR)lParam)->code)
|
|
{
|
|
case NM_CLICK: // Mouse
|
|
case NM_RETURN: // Keyboard
|
|
{
|
|
// Launch the ReleaseNotes URL (if it exists) with the default browser
|
|
if (!PhIsNullOrEmptyString(context->ReleaseNotesUrl))
|
|
PhShellExecute(hwndDlg, context->ReleaseNotesUrl->Buffer, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS ShowUpdateDialogThread(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
BOOL result;
|
|
MSG message;
|
|
PH_AUTO_POOL autoPool;
|
|
PPH_UPDATER_CONTEXT context = NULL;
|
|
|
|
if (Parameter)
|
|
context = (PPH_UPDATER_CONTEXT)Parameter;
|
|
else
|
|
context = CreateUpdateContext();
|
|
|
|
PhInitializeAutoPool(&autoPool);
|
|
|
|
UpdateDialogHandle = CreateDialogParam(
|
|
PluginInstance->DllBase,
|
|
MAKEINTRESOURCE(IDD_UPDATE),
|
|
PhMainWndHandle,
|
|
UpdaterWndProc,
|
|
(LPARAM)context
|
|
);
|
|
|
|
PhSetEvent(&InitializedEvent);
|
|
|
|
while (result = GetMessage(&message, NULL, 0, 0))
|
|
{
|
|
if (result == -1)
|
|
break;
|
|
|
|
if (!IsDialogMessage(UpdateDialogHandle, &message))
|
|
{
|
|
TranslateMessage(&message);
|
|
DispatchMessage(&message);
|
|
}
|
|
|
|
PhDrainAutoPool(&autoPool);
|
|
}
|
|
|
|
PhDeleteAutoPool(&autoPool);
|
|
PhResetEvent(&InitializedEvent);
|
|
|
|
if (UpdateDialogThreadHandle)
|
|
{
|
|
NtClose(UpdateDialogThreadHandle);
|
|
UpdateDialogThreadHandle = NULL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID ShowUpdateDialog(
|
|
_In_opt_ PPH_UPDATER_CONTEXT Context
|
|
)
|
|
{
|
|
if (!UpdateDialogThreadHandle)
|
|
{
|
|
if (!(UpdateDialogThreadHandle = PhCreateThread(0, ShowUpdateDialogThread, Context)))
|
|
{
|
|
PhShowStatus(PhMainWndHandle, L"Unable to create the updater window.", 0, GetLastError());
|
|
return;
|
|
}
|
|
|
|
PhWaitForEvent(&InitializedEvent, NULL);
|
|
}
|
|
|
|
PostMessage(UpdateDialogHandle, WM_SHOWDIALOG, 0, 0);
|
|
}
|
|
|
|
VOID StartInitialCheck(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE silentCheckThread = NULL;
|
|
|
|
if (silentCheckThread = PhCreateThread(0, UpdateCheckSilentThread, NULL))
|
|
NtClose(silentCheckThread);
|
|
} |