| Index: src/engine/Updater.cpp |
| =================================================================== |
| --- a/src/engine/Updater.cpp |
| +++ b/src/engine/Updater.cpp |
| @@ -1,18 +1,18 @@ |
| #include <functional> |
| #include <memory> |
| #include <sstream> |
| #include <Windows.h> |
| -#include <Msi.h> |
| #include <AdblockPlus/FileSystem.h> |
| #include <AdblockPlus/WebRequest.h> |
| +#include "../shared/AutoHandle.h" |
| #include "../shared/Dictionary.h" |
| #include "../shared/Utils.h" |
| #include "Debug.h" |
| #include "Resource.h" |
| #include "Updater.h" |
| namespace |
| { |
| @@ -80,68 +80,105 @@ namespace |
| } |
| DWORD RunThread(LPVOID param) |
| { |
| std::auto_ptr<ThreadCallbackType> callback(reinterpret_cast<ThreadCallbackType*>(param)); |
| (*callback)(); |
| return 0; |
| } |
| + |
| + std::wstring EscapeCommandLineArg(const std::wstring& arg) |
| + { |
| + // This does the inverse of CommandLineToArgvW(). See |
| + // http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx for |
| + // a description of the rules - the backslash rules are very non-obvious. |
| + std::wstring result = arg; |
| + size_t pos = arg.find(L'"'); |
| + while (pos != std::wstring::npos) |
| + { |
| + // Protect the quotation mark |
| + result.insert(pos, 1, L'\\'); |
| + pos++; |
| + |
| + // Protect any of the preceding backslashes |
| + for (int offset = -2; pos + offset >= 0 && result[pos + offset] == L'\\'; offset -= 2) |
| + { |
| + result.insert(pos + offset, 1, L'\\'); |
| + pos++; |
| + } |
| + |
| + // Find next quotation mark |
| + pos = arg.find(L'"', pos); |
| + } |
| + return L'"' + result + L'"'; |
| + } |
| + |
| + BOOL InstallUpdate(const std::wstring& path) |
| + { |
| + WCHAR sysDir[MAX_PATH]; |
| + UINT sysDirLen = GetSystemDirectoryW(sysDir, sizeof(sysDir) / sizeof(sysDir[0])); |
| + if (sysDirLen == 0) |
| + return false; |
| + |
| + std::wstring msiexec = std::wstring(sysDir, sysDirLen) + L"\\msiexec.exe"; |
| + |
| + std::wstring params = L"/i " + EscapeCommandLineArg(path) |
| + + L" ACTION=INSTALL INSTALLUILEVEL=2 REINSTALL=ALL" |
| + L" REINSTALLMODE=vomus MSIENFORCEUPGRADECOMPONENTRULES=1"; |
| + |
| + HINSTANCE instance = ShellExecuteW(NULL, L"runas", msiexec.c_str(), params.c_str(), NULL, SW_HIDE); |
| + if (reinterpret_cast<int>(instance) <= 32) |
| + return false; |
| + |
| + // As far as we are concerned everything is fine - MSI service will handle |
| + // further errors. |
| + return true; |
| + } |
| } |
| Updater::Updater(AdblockPlus::JsEnginePtr jsEngine, const std::string& url) |
| : jsEngine(jsEngine), url(url), tempFile(GetAppDataPath() + L"\\update.msi") |
| { |
| } |
| void Updater::Update() |
| { |
| Debug("Update available: " + url); |
| if (DialogBox(NULL, MAKEINTRESOURCE(IDD_UPDATEDIALOG), GetDesktopWindow(), |
| reinterpret_cast<DLGPROC>(&UpdateDlgProc)) == IDOK) |
| { |
| Debug("User accepted update"); |
| + DialogCallbackType* callback = new DialogCallbackType(std::bind(&Updater::StartDownload, |
| + this, std::placeholders::_1)); |
| + int result = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DOWNLOADDIALOG), GetDesktopWindow(), |
| + reinterpret_cast<DLGPROC>(&DownloadDlgProc), |
| + reinterpret_cast<LPARAM>(callback)); |
| + if (result == DOWNLOAD_FAILED) |
| { |
| - DialogCallbackType* callback = new DialogCallbackType(std::bind(&Updater::StartDownload, |
| - this, std::placeholders::_1)); |
| - int result = DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_DOWNLOADDIALOG), GetDesktopWindow(), |
| - reinterpret_cast<DLGPROC>(&DownloadDlgProc), |
| - reinterpret_cast<LPARAM>(callback)); |
| - if (result == DOWNLOAD_FAILED) |
| - { |
| - Dictionary* dict = Dictionary::GetInstance(); |
| - MessageBoxW(NULL, |
| - dict->Lookup("updater", "download-error-neterror").c_str(), |
| - dict->Lookup("updater", "download-error-title").c_str(), |
| - 0); |
| - } |
| - if (result != IDOK) |
| - return; |
| + Dictionary* dict = Dictionary::GetInstance(); |
| + MessageBoxW(NULL, |
| + dict->Lookup("updater", "download-error-neterror").c_str(), |
| + dict->Lookup("updater", "download-error-title").c_str(), |
| + 0); |
| } |
| + if (result != IDOK) |
| + return; |
| + if (!InstallUpdate(tempFile)) |
| { |
| - UINT result = ::MsiInstallProductW(tempFile.c_str(), L"ACTION=INSTALL INSTALLUILEVEL=2 REINSTALL=ALL REINSTALLMODE=vomus MSIENFORCEUPGRADECOMPONENTRULES=1"); |
| - if (result != ERROR_SUCCESS) |
| - { |
| - Dictionary* dict = Dictionary::GetInstance(); |
| - std::wstringstream message; |
| - message << dict->Lookup("updater", "download-error-runerror"); |
| - message << std::endl << L"(error " << result << L")"; |
| - MessageBoxW(NULL, |
| - message.str().c_str(), |
| - dict->Lookup("updater", "download-error-title").c_str(), |
| - 0); |
| + DebugLastError("Running updater failed"); |
| - std::stringstream error; |
| - error << "Installing update failed (error " << result << ")"; |
| - Debug(error.str()); |
| - return; |
| - } |
| + Dictionary* dict = Dictionary::GetInstance(); |
| + MessageBoxW(NULL, |
| + dict->Lookup("updater", "download-error-runerror").c_str(), |
| + dict->Lookup("updater", "download-error-title").c_str(), |
| + 0); |
| } |
| } |
| } |
| void Updater::StartDownload(HWND dialog) |
| { |
| this->dialog = dialog; |
| ThreadCallbackType* callback = new ThreadCallbackType(std::bind(&Updater::RunDownload, this)); |