overte-HifiExperiments/launchers/win32/LauncherUtils.cpp
2019-06-05 11:18:16 -07:00

405 lines
No EOL
13 KiB
C++

//
// LauncherUtils.cpp
//
// Created by Luis Cuenca on 6/5/2019.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "stdafx.h"
#include <wincrypt.h>
#include <tlhelp32.h>
#include <strsafe.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp")
#include "LauncherUtils.h"
BOOL LauncherUtils::IsProcessRunning(const wchar_t *processName) {
bool exists = false;
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry)) {
while (Process32Next(snapshot, &entry)) {
if (!_wcsicmp(entry.szExeFile, processName)) {
exists = true;
break;
}
}
}
CloseHandle(snapshot);
return exists;
}
HRESULT LauncherUtils::CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszArgs) {
IShellLink* psl;
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
CoInitialize(NULL);
HRESULT hres = E_INVALIDARG;
if ((lpszPathObj != NULL) && (wcslen(lpszPathObj) > 0) &&
(lpszDesc != NULL) &&
(lpszPathLink != NULL) && (strlen(lpszPathLink) > 0)) {
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres)) {
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
psl->SetArguments(lpszArgs);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres)) {
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return SUCCEEDED(hres);
}
std::string LauncherUtils::cStringToStd(CString cstring) {
CT2CA convertedString(cstring);
std::string strStd(convertedString);
return strStd;
}
BOOL LauncherUtils::launchApplication(LPCWSTR lpApplicationName, LPTSTR cmdArgs) {
// additional information
STARTUPINFO si;
PROCESS_INFORMATION pi;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
BOOL success = CreateProcess(
lpApplicationName, // the path
cmdArgs, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NEW_CONSOLE, // Opens file in a separate console
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure
);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return success;
}
BOOL LauncherUtils::deleteRegistryKey(const CString& registryPath) {
TCHAR szDelKey[MAX_PATH * 2];
StringCchCopy(szDelKey, MAX_PATH * 2, registryPath);
auto status = RegDeleteKey(HKEY_CURRENT_USER, szDelKey);
if (status == ERROR_SUCCESS) {
return TRUE;
}
return FALSE;
}
LauncherUtils::ResponseError LauncherUtils::makeHTTPCall(const CString& callerName,
const CString& mainUrl, const CString& dirUrl,
const CString& contentType, CStringA& postData,
CString& response, bool isPost = false) {
HINTERNET hopen = WinHttpOpen(callerName, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hopen) {
return ResponseError::Open;
}
HINTERNET hconnect = WinHttpConnect(hopen, mainUrl, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hconnect) {
return ResponseError::Connect;
}
HINTERNET hrequest = WinHttpOpenRequest(hconnect, isPost ? L"POST" : L"GET", dirUrl,
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hrequest) {
return ResponseError::OpenRequest;
}
if (isPost) {
if (!WinHttpSendRequest(hrequest, contentType, -1,
postData.GetBuffer(postData.GetLength()), (DWORD)strlen(postData), (DWORD)strlen(postData), NULL)) {
return ResponseError::SendRequest;
}
} else {
if (!WinHttpSendRequest(hrequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
return ResponseError::SendRequest;
}
}
if (!WinHttpReceiveResponse(hrequest, 0)) {
return ResponseError::ReceiveRequest;
}
// query remote file size, set haveContentLength on success and dwContentLength to the length
wchar_t szContentLength[32];
DWORD bufferBytes = 4096;
DWORD dwHeaderIndex = WINHTTP_NO_HEADER_INDEX;
BOOL haveContentLength = WinHttpQueryHeaders(hrequest, WINHTTP_QUERY_CONTENT_LENGTH, NULL,
&szContentLength, &bufferBytes, &dwHeaderIndex);
DWORD dwContentLength;
if (haveContentLength) {
dwContentLength = _wtoi(szContentLength);
}
byte p[4096];
if (!WinHttpQueryDataAvailable(hrequest, &bufferBytes)) {
return ResponseError::ReadResponse;
}
WinHttpReadData(hrequest, p, bufferBytes, &bufferBytes);
response = CString((const char*)p, (int)bufferBytes);
return ResponseError::NoError;
}
BOOL LauncherUtils::parseJSON(const CString& jsonTxt, Json::Value& root) {
Json::CharReaderBuilder CharBuilder;
std::string jsonString = cStringToStd(jsonTxt);
std::string errs;
Json::CharReader* nreader = CharBuilder.newCharReader();
bool parsingSuccessful = false;
if (nreader != NULL) {
parsingSuccessful = nreader->parse(jsonString.c_str(), jsonString.c_str() + jsonString.length(), &root, &errs);
delete nreader;
}
return parsingSuccessful;
}
BOOL LauncherUtils::getFont(const CString& fontName, int fontSize, bool isBold, CFont& fontOut) {
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = fontSize;
lf.lfWeight = isBold ? FW_BOLD : FW_NORMAL;
lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
lf.lfQuality = ANTIALIASED_QUALITY;
wcscpy_s(lf.lfFaceName, fontName);
if (!fontOut.CreateFontIndirect(&lf)) {
return FALSE;
}
return TRUE;
}
uint64_t LauncherUtils::extractZip(const std::string& zipFile, const std::string& path, std::vector<std::string>& files) {
mz_zip_archive zip_archive;
memset(&zip_archive, 0, sizeof(zip_archive));
auto status = mz_zip_reader_init_file(&zip_archive, zipFile.c_str(), 0);
if (!status) return 0;
int fileCount = (int)mz_zip_reader_get_num_files(&zip_archive);
if (fileCount == 0) {
mz_zip_reader_end(&zip_archive);
return 0;
}
mz_zip_archive_file_stat file_stat;
if (!mz_zip_reader_file_stat(&zip_archive, 0, &file_stat)) {
mz_zip_reader_end(&zip_archive);
return 0;
}
// Get root folder
CString lastDir = _T("");
uint64_t totalSize = 0;
for (int i = 0; i < fileCount; i++) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
std::string filename = file_stat.m_filename;
std::replace(filename.begin(), filename.end(), '/', '\\');
CString fullFilename = CString(path.c_str()) + "\\" + CString(filename.c_str());
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (SHCreateDirectoryEx(NULL, fullFilename, NULL) || ERROR_ALREADY_EXISTS == GetLastError()) {
break;
} else {
continue;
}
}
CT2A destFile(fullFilename);
if (mz_zip_reader_extract_to_file(&zip_archive, i, destFile, 0)) {
totalSize += (uint64_t)file_stat.m_uncomp_size;
files.emplace_back(destFile);
}
}
// Close the archive, freeing any resources it was using
mz_zip_reader_end(&zip_archive);
return totalSize;
}
BOOL LauncherUtils::insertRegistryKey(const std::string& regPath, const std::string& name, const std::string& value) {
HKEY key;
auto status = RegCreateKeyExA(HKEY_CURRENT_USER, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &key, NULL);
if (status == ERROR_SUCCESS) {
status = RegSetValueExA(key, name.c_str(), 0, REG_SZ, (const BYTE*)value.c_str(), (DWORD)(value.size() + 1));
return status == ERROR_SUCCESS;
}
RegCloseKey(key);
return FALSE;
}
BOOL LauncherUtils::insertRegistryKey(const std::string& regPath, const std::string& name, DWORD value) {
HKEY key;
auto status = RegCreateKeyExA(HKEY_CURRENT_USER, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE | KEY_QUERY_VALUE, NULL, &key, NULL);
if (status == ERROR_SUCCESS) {
status = RegSetValueExA(key, name.c_str(), 0, REG_DWORD, (const BYTE*)&value, sizeof(value));
return TRUE;
}
RegCloseKey(key);
return FALSE;
}
BOOL LauncherUtils::deleteFileOrDirectory(const CString& dirPath, bool noRecycleBin) {
CString dir = dirPath;
// Add extra null to string
dir.AppendChar(0);
SHFILEOPSTRUCT fileop;
fileop.hwnd = NULL; // no status display
fileop.wFunc = FO_DELETE; // delete operation
fileop.pFrom = dir; // source file name as double null terminated string
fileop.pTo = NULL; // no destination needed
fileop.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; // do not prompt the user
if (!noRecycleBin) {
fileop.fFlags |= FOF_ALLOWUNDO;
}
fileop.fAnyOperationsAborted = FALSE;
fileop.lpszProgressTitle = NULL;
fileop.hNameMappings = NULL;
int ret = SHFileOperation(&fileop);
return (ret == 0);
}
BOOL LauncherUtils::hMac256(const CString& cmessage, const char* keystr, CString& hashOut) {
char message[256];
strcpy_s(message, CStringA(cmessage).GetString());
char key[256];
strcpy_s(key, keystr);
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HCRYPTKEY hKey = 0;
HCRYPTHASH hHmacHash = 0;
BYTE pbHash[32];
HMAC_INFO HmacInfo;
int err = 0;
typedef struct blob {
BLOBHEADER header;
DWORD len;
BYTE key[1];
} m_blob;
ZeroMemory(&HmacInfo, sizeof(HmacInfo));
HmacInfo.HashAlgid = CALG_SHA_256;
ZeroMemory(&pbHash, 32);
m_blob* kb = NULL;
DWORD kbSize = DWORD(sizeof(m_blob) + strlen(key));
kb = (m_blob*)malloc(kbSize);
kb->header.bType = PLAINTEXTKEYBLOB;
kb->header.bVersion = CUR_BLOB_VERSION;
kb->header.reserved = 0;
kb->header.aiKeyAlg = CALG_RC2;
memcpy(&kb->key, key, strlen(key));
kb->len = (DWORD)strlen(key);
BOOL success = false;
DWORD datalen = (DWORD)32;
if (CryptAcquireContext(&hProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET)
&& CryptImportKey(hProv, (BYTE*)kb, kbSize, 0, CRYPT_IPSEC_HMAC_KEY, &hKey)
&& CryptCreateHash(hProv, CALG_HMAC, hKey, 0, &hHmacHash)
&& CryptSetHashParam(hHmacHash, HP_HMAC_INFO, (BYTE*)&HmacInfo, 0)
&& CryptHashData(hHmacHash, (BYTE*)message, (DWORD)strlen(message), 0)
&& CryptGetHashParam(hHmacHash, HP_HASHVAL, pbHash, &datalen, 0)) {
char *Hex = "0123456789abcdef";
char HashString[65] = { 0 };
for (int Count = 0; Count < 32; Count++)
{
HashString[Count * 2] = Hex[pbHash[Count] >> 4];
HashString[(Count * 2) + 1] = Hex[pbHash[Count] & 0xF];
}
hashOut = CString(HashString);
success = true;
}
free(kb);
if (hHmacHash)
CryptDestroyHash(hHmacHash);
if (hKey)
CryptDestroyKey(hKey);
if (hHash)
CryptDestroyHash(hHash);
if (hProv)
CryptReleaseContext(hProv, 0);
return success;
}
DWORD WINAPI LauncherUtils::unzipThread(LPVOID lpParameter) {
UnzipThreadData& data = *((UnzipThreadData*)lpParameter);
uint64_t size = LauncherUtils::extractZip(data._zipFile, data._path, std::vector<std::string>());
int mb_size = (int)(size * 0.001f);
data.callback(data._type, mb_size);
delete &data;
return 0;
}
DWORD WINAPI LauncherUtils::downloadThread(LPVOID lpParameter)
{
DownloadThreadData& data = *((DownloadThreadData*)lpParameter);
auto hr = URLDownloadToFile(0, data._url, data._file, 0, NULL);
data.callback(data._type);
return 0;
}
BOOL LauncherUtils::unzipFileOnThread(int type, const std::string& zipFile, const std::string& path, std::function<void(int, int)> callback) {
DWORD myThreadID;
UnzipThreadData* unzipThreadData = new UnzipThreadData();
unzipThreadData->_type = type;
unzipThreadData->_zipFile = zipFile;
unzipThreadData->_path = path;
unzipThreadData->setCallback(callback);
HANDLE myHandle = CreateThread(0, 0, unzipThread, unzipThreadData, 0, &myThreadID);
if (myHandle) {
CloseHandle(myHandle);
return TRUE;
}
return FALSE;
}
BOOL LauncherUtils::downloadFileOnThread(int type, const CString& url, const CString& file, std::function<void(int)> callback) {
DWORD myThreadID;
DownloadThreadData* downloadThreadData = new DownloadThreadData();
downloadThreadData->_type = type;
downloadThreadData->_url = url;
downloadThreadData->_file = file;
downloadThreadData->setCallback(callback);
HANDLE myHandle = CreateThread(0, 0, downloadThread, downloadThreadData, 0, &myThreadID);
if (myHandle) {
CloseHandle(myHandle);
return TRUE;
}
return FALSE;
}