/*
* 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 .
*/
#include "..\main.h"
#include
PPH_UPDATER_CONTEXT CreateUpdateContext(
_In_ PPLUGIN_NODE Node,
_In_ PLUGIN_ACTION Action
)
{
PPH_UPDATER_CONTEXT context;
context = (PPH_UPDATER_CONTEXT)PhCreateAlloc(sizeof(PH_UPDATER_CONTEXT));
memset(context, 0, sizeof(PH_UPDATER_CONTEXT));
context->Action = Action;
context->Node = Node;
return context;
}
VOID FreeUpdateContext(
_In_ _Post_invalid_ PPH_UPDATER_CONTEXT Context
)
{
//PhClearReference(&Context->Version);
PhClearReference(&Context->RevVersion);
//PhClearReference(&Context->RelDate);
PhClearReference(&Context->Size);
//PhClearReference(&Context->Signature);
//PhClearReference(&Context->ReleaseNotesUrl);
PhClearReference(&Context->SetupFilePath);
PhClearReference(&Context->FileDownloadUrl);
PhDereferenceObject(Context);
}
VOID TaskDialogCreateIcons(
_In_ PPH_UPDATER_CONTEXT Context
)
{
// Load the Process Hacker window icon
Context->IconLargeHandle = (HICON)LoadImage(
NtCurrentPeb()->ImageBaseAddress,
MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER),
IMAGE_ICON,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON),
LR_SHARED
);
Context->IconSmallHandle = (HICON)LoadImage(
NtCurrentPeb()->ImageBaseAddress,
MAKEINTRESOURCE(PHAPP_IDI_PROCESSHACKER),
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_SHARED
);
// Set the TaskDialog window icons
SendMessage(Context->DialogHandle, WM_SETICON, ICON_SMALL, (LPARAM)Context->IconSmallHandle);
SendMessage(Context->DialogHandle, WM_SETICON, ICON_BIG, (LPARAM)Context->IconLargeHandle);
}
VOID TaskDialogLinkClicked(
_In_ PPH_UPDATER_CONTEXT Context
)
{
//if (!PhIsNullOrEmptyString(Context->ReleaseNotesUrl))
{
// Launch the ReleaseNotes URL (if it exists) with the default browser
//PhShellExecute(Context->DialogHandle, Context->ReleaseNotesUrl->Buffer, NULL);
}
}
PPH_STRING UpdaterGetOpaqueXmlNodeText(
_In_ mxml_node_t *xmlNode
)
{
if (xmlNode && xmlNode->child && xmlNode->child->type == MXML_OPAQUE && xmlNode->child->value.opaque)
{
return PhConvertUtf8ToUtf16(xmlNode->child->value.opaque);
}
return PhReferenceEmptyString();
}
BOOLEAN LastUpdateCheckExpired(
VOID
)
{
#ifdef FORCE_UPDATE_CHECK
return TRUE;
#else
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);
// P//hDereferenceObject(lastUpdateTimeString);
return TRUE;
}
// Cleanup
//PhDereferenceObject(lastUpdateTimeString);
return FALSE;
#endif
}
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->VersionString->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;
}
BOOLEAN QueryUpdateData(
_Inout_ PPH_UPDATER_CONTEXT Context
)
{
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 (!(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 (WindowsVersion >= WINDOWS_7)
{
ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE;
WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG));
}
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, MXML_OPAQUE_CALLBACK);
if (xmlNode == NULL || xmlNode->type != MXML_ELEMENT)
__leave;
// Find the version node
/* Context->Version = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "ver", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->Version)*/
__leave;
// Find the revision node
Context->RevVersion = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "rev", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->RevVersion))
__leave;
// Find the release date node
/* Context->RelDate = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "reldate", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->RelDate))
__leave;*/
// Find the size node
Context->Size = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "size", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->Size))
__leave;
// Find the Hash node
//Context->Hash = UpdaterGetOpaqueXmlNodeText(
// mxmlFindElement(xmlNode->child, xmlNode, "sha2", NULL, NULL, MXML_DESCEND)
// );
//if (PhIsNullOrEmptyString(Context->Hash))
// __leave;
// Find the signature node
/*Context->Signature = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "sig", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->Signature))
__leave;*/
// Find the release notes URL
/*Context->ReleaseNotesUrl = UpdaterGetOpaqueXmlNodeText(
mxmlFindElement(xmlNode->child, xmlNode, "relnotes", NULL, NULL, MXML_DESCEND)
);
if (PhIsNullOrEmptyString(Context->ReleaseNotesUrl))
__leave;*/
// Find the installer download URL
/*Context->SetupFileDownloadUrl = UpdaterGetOpaqueXmlNodeText(
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 UpdateDownloadThread(
_In_ PVOID Parameter
)
{
BOOLEAN downloadSuccess = FALSE;
BOOLEAN hashSuccess = FALSE;
BOOLEAN signatureSuccess = 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;
PUPDATER_HASH_CONTEXT hashContext = NULL;
ULONG indexOfFileName = -1;
GUID randomGuid;
URL_COMPONENTS httpUrlComponents = { sizeof(URL_COMPONENTS) };
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxyConfig = { 0 };
LARGE_INTEGER timeNow;
LARGE_INTEGER timeStart;
ULONG64 timeTicks = 0;
ULONG64 timeBitsPerSecond = 0;
PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)Parameter;
context->FileDownloadUrl = PhFormatString(
L"https://wj32.org/processhacker/plugins/download.php?id=%s&type=64",
PhGetStringOrEmpty(context->Node->Id)
);
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Initializing download request...");
// Create a user agent string.
//userAgentString = PhFormatString(
// L"PH_%lu.%lu_%lu",
// context->CurrentMajorVersion,
// context->CurrentMinorVersion,
// context->CurrentRevisionVersion
// );
//if (PhIsNullOrEmptyString(userAgentString))
// goto CleanupExit;
setupTempPath = PhCreateStringEx(NULL, GetTempPath(0, NULL) * sizeof(WCHAR));
if (PhIsNullOrEmptyString(setupTempPath))
goto CleanupExit;
if (GetTempPath((ULONG)setupTempPath->Length / sizeof(WCHAR), setupTempPath->Buffer) == 0)
goto CleanupExit;
if (PhIsNullOrEmptyString(setupTempPath))
goto CleanupExit;
// 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\\%s.zip",
PhGetStringOrEmpty(setupTempPath),
PhGetStringOrEmpty(randomGuidString),
PhGetStringOrEmpty(context->Node->InternalName)
);
if (PhIsNullOrEmptyString(context->SetupFilePath))
goto CleanupExit;
// Create the directory if it does not exist.
if (fullSetupPath = PhGetFullPath(PhGetString(context->SetupFilePath), &indexOfFileName))
{
PPH_STRING directoryPath;
if (indexOfFileName == -1)
goto CleanupExit;
if (directoryPath = PhSubstring(fullSetupPath, 0, indexOfFileName))
{
SHCreateDirectoryEx(NULL, PhGetString(directoryPath), NULL);
PhDereferenceObject(directoryPath);
}
}
// Create output file
if (!NT_SUCCESS(PhCreateFileWin32(
&tempFileHandle,
PhGetStringOrEmpty(context->SetupFilePath),
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
)))
{
goto CleanupExit;
}
// 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(
PhGetStringOrEmpty(context->FileDownloadUrl),
0,
0,
&httpUrlComponents
))
{
goto CleanupExit;
}
// Create the Host string.
downloadHostPath = PhCreateStringEx(httpUrlComponents.lpszHostName, httpUrlComponents.dwHostNameLength * sizeof(WCHAR));
// Create the Path string.
downloadUrlPath = PhCreateStringEx(httpUrlComponents.lpszUrlPath, httpUrlComponents.dwUrlPathLength * sizeof(WCHAR));
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Connecting...");
// Query the current system proxy
WinHttpGetIEProxyConfigForCurrentUser(&proxyConfig);
// Open the HTTP session with the system proxy configuration if available
if (!(httpSessionHandle = WinHttpOpen(
PhGetStringOrEmpty(userAgentString),
proxyConfig.lpszProxy != NULL ? WINHTTP_ACCESS_TYPE_NAMED_PROXY : WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
proxyConfig.lpszProxy,
proxyConfig.lpszProxyBypass,
0
)))
{
goto CleanupExit;
}
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,
PhGetStringOrEmpty(downloadHostPath),
httpUrlComponents.nScheme == INTERNET_SCHEME_HTTP ? INTERNET_DEFAULT_HTTP_PORT : INTERNET_DEFAULT_HTTPS_PORT,
0
)))
{
goto CleanupExit;
}
if (!(httpRequestHandle = WinHttpOpenRequest(
httpConnectionHandle,
NULL,
PhGetStringOrEmpty(downloadUrlPath),
NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH | (httpUrlComponents.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE : 0)
)))
{
goto CleanupExit;
}
if (WindowsVersion >= WINDOWS_7)
{
ULONG keepAlive = WINHTTP_DISABLE_KEEP_ALIVE;
WinHttpSetOption(httpRequestHandle, WINHTTP_OPTION_DISABLE_FEATURE, &keepAlive, sizeof(ULONG));
}
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Sending download request...");
if (!WinHttpSendRequest(
httpRequestHandle,
WINHTTP_NO_ADDITIONAL_HEADERS,
0,
WINHTTP_NO_REQUEST_DATA,
0,
WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH,
0
))
{
goto CleanupExit;
}
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)L"Waiting for response...");
if (WinHttpReceiveResponse(httpRequestHandle, NULL))
{
ULONG bytesDownloaded = 0;
ULONG downloadedBytes = 0;
ULONG contentLengthSize = sizeof(ULONG);
ULONG contentLength = 0;
PPH_STRING status;
IO_STATUS_BLOCK isb;
BYTE buffer[PAGE_SIZE];
status = PhFormatString(L"Downloading %s...", PhGetString(context->Node->Name));
SendMessage(context->DialogHandle, TDM_SET_MARQUEE_PROGRESS_BAR, FALSE, 0);
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_MAIN_INSTRUCTION, (LPARAM)PhGetString(status));
PhDereferenceObject(status);
// Start the clock.
PhQuerySystemTime(&timeStart);
if (!WinHttpQueryHeaders(
httpRequestHandle,
WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&contentLength,
&contentLengthSize,
0
))
{
goto CleanupExit;
}
// Initialize hash algorithm.
if (!UpdaterInitializeHash(&hashContext))
goto CleanupExit;
// 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 (!UpdateDialogThreadHandle)
// __leave;
// Update the hash of bytes we downloaded.
UpdaterUpdateHash(hashContext, buffer, bytesDownloaded);
// Write the downloaded bytes to disk.
if (!NT_SUCCESS(NtWriteFile(
tempFileHandle,
NULL,
NULL,
NULL,
&isb,
buffer,
bytesDownloaded,
NULL,
NULL
)))
{
goto CleanupExit;
}
downloadedBytes += (DWORD)isb.Information;
// Check the number of bytes written are the same we downloaded.
if (bytesDownloaded != isb.Information)
goto CleanupExit;
// Query the current time
PhQuerySystemTime(&timeNow);
// Calculate the number of ticks
timeTicks = (timeNow.QuadPart - timeStart.QuadPart) / PH_TICKS_PER_SEC;
timeBitsPerSecond = downloadedBytes / __max(timeTicks, 1);
// TODO: Update on timer callback.
{
FLOAT percent = ((FLOAT)downloadedBytes / contentLength * 100);
PPH_STRING totalLength = PhFormatSize(contentLength, -1);
PPH_STRING totalDownloaded = PhFormatSize(downloadedBytes, -1);
PPH_STRING totalSpeed = PhFormatSize(timeBitsPerSecond, -1);
PPH_STRING statusMessage = PhFormatString(
L"Downloaded: %s of %s (%.0f%%)\r\nSpeed: %s/s",
totalDownloaded->Buffer,
totalLength->Buffer,
percent,
totalSpeed->Buffer
);
SendMessage(context->DialogHandle, TDM_SET_PROGRESS_BAR_POS, (WPARAM)percent, 0);
SendMessage(context->DialogHandle, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM)statusMessage->Buffer);
PhDereferenceObject(statusMessage);
PhDereferenceObject(totalSpeed);
PhDereferenceObject(totalLength);
PhDereferenceObject(totalDownloaded);
}
}
downloadSuccess = TRUE;
if (UpdaterVerifyHash(hashContext, context->Node->SHA2_64))
{
hashSuccess = TRUE;
}
if (UpdaterVerifySignature(hashContext, context->Node->HASH_64))
{
signatureSuccess = TRUE;
}
}
CleanupExit:
if (hashContext)
UpdaterDestroyHash(hashContext);
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 (downloadSuccess && hashSuccess && signatureSuccess)
{
if (NT_SUCCESS(SetupExtractBuild(context)))
{
PostMessage(context->DialogHandle, PH_UPDATESUCCESS, 0, 0);
}
else
{
PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0);
}
}
else
{
PostMessage(context->DialogHandle, PH_UPDATEISERRORED, 0, 0);
}
return STATUS_SUCCESS;
}
LRESULT CALLBACK TaskDialogSubclassProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ UINT_PTR uIdSubclass,
_In_ ULONG_PTR dwRefData
)
{
PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData;
switch (uMsg)
{
case WM_NCDESTROY:
{
RemoveWindowSubclass(hwndDlg, TaskDialogSubclassProc, uIdSubclass);
}
break;
case WM_APP + 1:
{
if (IsIconic(hwndDlg))
ShowWindow(hwndDlg, SW_RESTORE);
else
ShowWindow(hwndDlg, SW_SHOW);
SetForegroundWindow(hwndDlg);
}
break;
case PH_UPDATEAVAILABLE:
{
ShowAvailableDialog(context);
}
break;
case PH_UPDATEISCURRENT:
{
ShowLatestVersionDialog(context);
}
break;
case PH_UPDATENEWER:
{
ShowUninstallRestartDialog(context);
}
break;
case PH_UPDATESUCCESS:
{
ShowInstallRestartDialog(context);
}
break;
case PH_UPDATEFAILURE:
{
if ((BOOLEAN)wParam)
ShowUpdateFailedDialog(context, TRUE, FALSE);
else if ((BOOLEAN)lParam)
ShowUpdateFailedDialog(context, FALSE, TRUE);
else
ShowUpdateFailedDialog(context, FALSE, FALSE);
}
break;
case PH_UPDATEISERRORED:
{
ShowUpdateFailedDialog(context, FALSE, FALSE);
}
break;
//case WM_PARENTNOTIFY:
// {
// if (wParam == WM_CREATE)
// {
// // uMsg == 49251 for expand/collapse button click
// HWND hwndEdit = CreateWindowEx(
// WS_EX_CLIENTEDGE,
// L"EDIT",
// NULL,
// WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
// 5,
// 5,
// 390,
// 85,
// (HWND)lParam, // parent window
// 0,
// NULL,
// NULL
// );
//
// CommonCreateFont(-11, hwndEdit);
//
// // Add text to the window.
// SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)L"TEST");
// }
// }
// break;
//case WM_NCACTIVATE:
// {
// if (IsWindowVisible(PhMainWndHandle) && !IsMinimized(PhMainWndHandle))
// {
// if (!context->FixedWindowStyles)
// {
// SetWindowLongPtr(hwndDlg, GWLP_HWNDPARENT, (LONG_PTR)PhMainWndHandle);
// PhSetWindowExStyle(hwndDlg, WS_EX_APPWINDOW, WS_EX_APPWINDOW);
// context->FixedWindowStyles = TRUE;
// }
// }
// }
// break;
}
return DefSubclassProc(hwndDlg, uMsg, wParam, lParam);
}
HRESULT CALLBACK TaskDialogBootstrapCallback(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam,
_In_ LONG_PTR dwRefData
)
{
PPH_UPDATER_CONTEXT context = (PPH_UPDATER_CONTEXT)dwRefData;
switch (uMsg)
{
case TDN_CREATED:
{
context->DialogHandle = hwndDlg;
TaskDialogCreateIcons(context);
SetWindowSubclass(hwndDlg, TaskDialogSubclassProc, 0, (ULONG_PTR)context);
switch (context->Action)
{
case PLUGIN_ACTION_INSTALL:
{
ShowAvailableDialog(context);
}
break;
case PLUGIN_ACTION_UNINSTALL:
{
ShowPluginUninstallDialog(context);
}
break;
case PLUGIN_ACTION_RESTART:
{
ShowUninstallRestartDialog(context);
}
break;
}
}
break;
}
return S_OK;
}
BOOLEAN ShowInitialDialog(
_In_ HWND Parent,
_In_ PVOID Context
)
{
INT result = 0;
TASKDIALOGCONFIG config = { sizeof(TASKDIALOGCONFIG) };
config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION | TDF_CAN_BE_MINIMIZED | TDF_POSITION_RELATIVE_TO_WINDOW;
config.pszContent = L"Initializing...";
config.lpCallbackData = (LONG_PTR)Context;
config.pfCallback = TaskDialogBootstrapCallback;
config.hwndParent = Parent;
// Start the TaskDialog bootstrap
TaskDialogIndirect(&config, &result, NULL, NULL);
return result == IDOK;
}
BOOLEAN ShowUpdateDialog(
_In_ HWND Parent,
_In_ PLUGIN_ACTION Action
)
{
BOOLEAN result;
PH_AUTO_POOL autoPool;
PPH_UPDATER_CONTEXT context;
context = CreateUpdateContext(NULL, Action);
PhInitializeAutoPool(&autoPool);
result = ShowInitialDialog(Parent, context);
FreeUpdateContext(context);
PhDeleteAutoPool(&autoPool);
return result;
}
BOOLEAN StartInitialCheck(
_In_ HWND Parent,
_In_ PPLUGIN_NODE Node,
_In_ PLUGIN_ACTION Action
)
{
BOOLEAN result;
PH_AUTO_POOL autoPool;
PPH_UPDATER_CONTEXT context;
context = CreateUpdateContext(Node, Action);
PhInitializeAutoPool(&autoPool);
result = ShowInitialDialog(Parent, context);
FreeUpdateContext(context);
PhDeleteAutoPool(&autoPool);
return result;
}