Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // EndlessUsbToolDlg.cpp : implementation file | |
| // | |
| #include "stdafx.h" | |
| #include "EndlessUsbTool.h" | |
| #include "EndlessUsbToolDlg.h" | |
| #include "Analytics.h" | |
| #include "afxdialogex.h" | |
| #include <functional> | |
| #include <windowsx.h> | |
| #include <dbt.h> | |
| #include <atlpath.h> | |
| #include <intrin.h> | |
| #include <Aclapi.h> | |
| #include <Wbemidl.h> | |
| #include "json/json.h" | |
| #include <fstream> | |
| #include "Version.h" | |
| #include "WindowsUsbDefines.h" | |
| #include "StringHelperMethods.h" | |
| #include "Images.h" | |
| // Rufus include files | |
| extern "C" { | |
| #include "rufus.h" | |
| #include "missing.h" | |
| #include "msapi_utf8.h" | |
| #include "drive.h" | |
| #include "bled/bled.h" | |
| #include "file.h" | |
| #include "ms-sys/inc/br.h" | |
| #include "usb.h" | |
| #include "mbr_grub2.h" | |
| #include "grub/grub.h" | |
| // RADU: try to remove the need for all of these | |
| OPENED_LIBRARIES_VARS; | |
| HWND hMainDialog = NULL, hLog = NULL; | |
| BOOL right_to_left_mode = FALSE; | |
| GetTickCount64_t pfGetTickCount64 = NULL; | |
| RUFUS_UPDATE update = { { 0,0,0 },{ 0,0 }, NULL, NULL }; | |
| char *ini_file = NULL; | |
| BOOL usb_debug = FALSE; | |
| WORD selected_langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); | |
| extern const loc_control_id control_id[]; | |
| extern loc_dlg_list loc_dlg[]; | |
| extern char** msg_table; | |
| // Trying to reuse as much as rufus as possible | |
| HWND hDeviceList = NULL, hBootType = NULL, hPartitionScheme = NULL, hFileSystem = NULL, hClusterSize = NULL, hLabel = NULL; | |
| HWND hNBPasses = NULL, hDiskID = NULL, hInfo = NULL; | |
| HINSTANCE hMainInstance = NULL; | |
| BOOL detect_fakes = FALSE; | |
| char system_dir[MAX_PATH], sysnative_dir[MAX_PATH]; | |
| char* image_path = NULL; | |
| // Number of steps for each FS for FCC_STRUCTURE_PROGRESS | |
| const int nb_steps[FS_MAX] = { 5, 5, 12, 1, 10 }; | |
| BOOL format_op_in_progress = FALSE; | |
| BOOL allow_dual_uefi_bios, togo_mode, force_large_fat32, enable_ntfs_compression = FALSE, lock_drive = TRUE; | |
| BOOL zero_drive = FALSE, preserve_timestamps, list_non_usb_removable_drives = FALSE; | |
| BOOL use_own_c32[NB_OLD_C32] = { FALSE, FALSE }; | |
| uint16_t rufus_version[3], embedded_sl_version[2]; | |
| char embedded_sl_version_str[2][12] = { "?.??", "?.??" }; | |
| char embedded_sl_version_ext[2][32]; | |
| uint32_t dur_mins, dur_secs; | |
| StrArray DriveID, DriveLabel; | |
| BOOL enable_HDDs = TRUE, use_fake_units, enable_vmdk; | |
| int dialog_showing = 0; | |
| PF_TYPE_DECL(WINAPI, BOOL, SHChangeNotifyDeregister, (ULONG)); | |
| PF_TYPE_DECL(WINAPI, ULONG, SHChangeNotifyRegister, (HWND, int, LONG, UINT, int, const SHChangeNotifyEntry *)); | |
| PF_TYPE_DECL(WINAPI, BOOL, ChangeWindowMessageFilterEx, (HWND, UINT, DWORD, PCHANGEFILTERSTRUCT)); | |
| BOOL FormatDrive(DWORD DriveIndex, int fsToUse, const wchar_t *partLabel); | |
| // Added by us so we don't go through the hastle of getting device speed again | |
| // Rufus code already does it | |
| DWORD usbDeviceSpeed[128]; | |
| BOOL usbDeviceSpeedIsLower[128]; | |
| DWORD usbDevicesCount; | |
| }; | |
| #include "localization.h" | |
| // End Rufus include files | |
| #include "gpt/gpt.h" | |
| #include "PGPSignature.h" | |
| #ifdef _DEBUG | |
| #define new DEBUG_NEW | |
| #endif | |
| #define HTML_BUTTON_ID(__id__) (__id__##"C") | |
| // HTML element ids and classes | |
| // pages | |
| #define ELEMENT_DUALBOOT_PAGE "DualBootInstallPage" | |
| #define ELEMENT_ADVANCED_PAGE "AdvancedPage" | |
| #define ELEMENT_FILE_PAGE "SelectFilePage" | |
| #define ELEMENT_USB_PAGE "SelectUSBPage" | |
| #define ELEMENT_STORAGE_PAGE "SelectStoragePage" | |
| #define ELEMENT_INSTALL_PAGE "InstallingPage" | |
| #define ELEMENT_SUCCESS_PAGE "ThankYouPage" | |
| #define ELEMENT_ERROR_PAGE "ErrorPage" | |
| //classes | |
| #define CLASS_PAGE_HEADER_TITLE "PageHeaderTitle" | |
| #define CLASS_PAGE_HEADER "PageHeader" | |
| #define CLASS_BUTTON_DISABLED "ButtonDisabled" | |
| //Dual boot elements | |
| #define ELEMENT_LANGUAGE_DUALBOOT "LanguageSelectDualBoot" | |
| #define ELEMENT_DUALBOOT_CLOSE_BUTTON "DualBootPageCloseButton" | |
| #define ELEMENT_DUALBOOT_ADVANCED_TEXT "AdvancedOptionsText" | |
| #define ELEMENT_DUALBOOT_ADVANCED_LINK "AdvancedOptionsLink" | |
| #define ELEMENT_DUALBOOT_INSTALL_BUTTON "DualBootInstallButton" | |
| #define ELEMENT_DUALBOOT_TITLE "DualBootContentTitle" | |
| #define ELEMENT_DUALBOOT_DESCRIPTION "DualBootContentDescription" | |
| #define ELEMENT_DUALBOOT_RECOMMENDATION "DualBootRecommendation" | |
| //Advanced page elements | |
| #define ELEMENT_LIVE_USB_BUTTON "LiveUsbButton" | |
| #define ELEMENT_REFORMATTER_USB_BUTTON "ReformatterUsbButton" | |
| #define ELEMENT_COMPARE_OPTIONS "CompareOptionsLink" | |
| #define ELEMENT_ADVANCED_CLOSE_BUTTON "AdvancedPageCloseButton" | |
| #define ELEMENT_ADVANCED_PREV_BUTTON "AdvancedPagePreviousButton" | |
| #define ELEMENT_ADVOPT_SUBTITLE "AdvOptSubtitleContainer" | |
| #define ELEMENT_COMBINED_USB_BUTTON "CombinedUsbButton" | |
| #define ELEMENT_USB_LEARN_MORE "USBLearnMore" | |
| //Select File page elements | |
| #define ELEMENT_SELFILE_PREV_BUTTON "SelectFilePreviousButton" | |
| #define ELEMENT_SELFILE_NEXT_BUTTON "SelectFileNextButton" | |
| #define ELEMENT_FILES_SELECT "LocalImagesSelect" | |
| #define ELEMENT_REMOTE_SELECT "OnlineImagesSelect" | |
| #define ELEMENT_IMAGE_TYPE_LOCAL "OperatingSystemTypeLocal" | |
| #define ELEMENT_IMAGE_TYPE_REMOTE "OperatingSystemTypeOnline" | |
| #define ELEMENT_SELFILE_NO_CONNECTION "NoInternetConnection" | |
| #define ELEMENT_SELFILE_DOWN_LANG "DownloadLanguageSelect" | |
| #define ELEMENT_LOCAL_FILES_FOUND "SelectFileLocalPresent" | |
| #define ELEMENT_LOCAL_FILES_NOT_FOUND "SelectFileLocalNotPresent" | |
| #define ELEMENT_DOWNLOAD_LIGHT_BUTTON "DownloadLightButton" | |
| #define ELEMENT_DOWNLOAD_FULL_BUTTON "DownloadFullButton" | |
| #define ELEMENT_DOWNLOAD_LIGHT_SIZE "LightDownloadSubtitle" | |
| #define ELEMENT_DOWNLOAD_FULL_SIZE "FullDownloadSubtitle" | |
| #define ELEMENT_CONNECTED_LINK "ConnectedLink" | |
| #define ELEMENT_CONNECTED_SUPPORT_LINK "ConnectedSupportLink" | |
| #define ELEMENT_SET_FILE_TITLE "SelectFilePageTitle" | |
| #define ELEMENT_SET_FILE_SUBTITLE "SelectFileSubtitle" | |
| //Select USB page elements | |
| #define ELEMENT_SELUSB_PREV_BUTTON "SelectUSBPreviousButton" | |
| #define ELEMENT_SELUSB_NEXT_BUTTON "SelectUSBNextButton" | |
| #define ELEMENT_SELUSB_USB_DRIVES "USBDiskSelect" | |
| #define ELEMENT_SELUSB_NEW_DISK_NAME "NewDiskName" | |
| #define ELEMENT_SELUSB_NEW_DISK_SIZE "NewDiskSize" | |
| #define ELEMENT_SELUSB_AGREEMENT "AgreementCheckbox" | |
| #define ELEMENT_SELUSB_SPEEDWARNING "UsbSpeedWarning" | |
| //Select Storage page elements | |
| #define ELEMENT_SELSTORAGE_PREV_BUTTON "SelectStoragePreviousButton" | |
| #define ELEMENT_SELSTORAGE_NEXT_BUTTON "SelectStorageNextButton" | |
| #define ELEMENT_STORAGE_SELECT "StorageSpaceSelect" | |
| #define ELEMENT_STORAGE_DESCRIPTION "StorageSpaceDescription" | |
| #define ELEMENT_STORAGE_AVAILABLE "SelectStorageAvailableSpace" | |
| #define ELEMENT_STORAGE_MESSAGE "SelectStorageSubtitle" | |
| #define ELEMENT_STORAGE_AGREEMENT_TEXT "StorageAgreementText" | |
| #define ELEMENT_STORAGE_SPACE_WARNING "StorageSpaceWarning" | |
| #define ELEMENT_STORAGE_SUPPORT_LINK "StorageSupportLink" | |
| //Installing page elements | |
| #define ELEMENT_INSTALL_STATUS "InstallStepStatus" | |
| #define ELEMENT_INSTALL_STEP "CurrentStepText" | |
| #define ELEMENT_INSTALL_STEPS_TOTAL "TotalStepsText" | |
| #define ELEMENT_INSTALL_STEP_TEXT "CurrentStepDescription" | |
| #define ELEMENT_INSTALL_CANCEL "InstallCancelButton" | |
| //Thank You page elements | |
| #define ELEMENT_CLOSE_BUTTON "CloseAppButton" | |
| #define ELEMENT_INSTALLER_VERSION "InstallerVersionValue" | |
| #define ELEMENT_INSTALLER_LANGUAGE_ROW "InstallerLanguageRow" | |
| #define ELEMENT_INSTALLER_LANGUAGE "InstallerLanguageValue" | |
| #define ELEMENT_INSTALLER_CONTENT "InstallerContentValue" | |
| #define ELEMENT_THANKYOU_MESSAGE "ThankYouMessage" | |
| #define ELEMENT_DUALBOOT_REMINDER "ThankYouDualBootReminder" | |
| #define ELEMENT_LIVE_REMINDER "ThankYouLiveReminder" | |
| #define ELEMENT_REFLASHER_REMINDER "ThankYouReflasherReminder" | |
| #define ELEMENT_USBBOOT_HOWTO "UsbBootHowToLink" | |
| //Error page | |
| #define ELEMENT_ERROR_MESSAGE "ErrorMessage" | |
| #define ELEMENT_ERROR_CLOSE_BUTTON "CloseAppButton1" | |
| #define ELEMENT_ENDLESS_SUPPORT "EndlessSupport" | |
| #define ELEMENT_ERROR_SUGGESTION "ErrorMessageSuggestion" | |
| #define ELEMENT_ERROR_BUTTON "ErrorContinueButton" | |
| #define ELEMENT_ERROR_DELETE_CHECKBOX "DeleteFilesCheckbox" | |
| #define ELEMENT_ERROR_DELETE_TEXT "DeleteFilesText" | |
| #define ELEMENT_VERSION_LINK "VersionLink" | |
| // Javascript methods | |
| #define JS_SET_PROGRESS "setProgress" | |
| #define JS_ENABLE_DOWNLOAD "enableDownload" | |
| #define JS_ENABLE_ELEMENT "enableElement" | |
| #define JS_ENABLE_BUTTON "enableButton" | |
| #define JS_SHOW_ELEMENT "showElement" | |
| #define JS_RESET_CHECK "resetCheck" | |
| // Personalities | |
| #define PERSONALITY_BASE L"base" | |
| #define PERSONALITY_ENGLISH L"en" | |
| #define PERSONALITY_SPANISH L"es" | |
| #define PERSONALITY_PORTUGUESE L"pt_BR" | |
| #define PERSONALITY_ARABIC L"ar" | |
| #define PERSONALITY_FRENCH L"fr" | |
| #define PERSONALITY_CHINESE L"zh_CN" | |
| #define PERSONALITY_BENGALI L"bn" | |
| #define PERSONALITY_SPANISH_GT L"es_GT" | |
| static const wchar_t *globalAvailablePersonalities[] = | |
| { | |
| PERSONALITY_BASE, | |
| PERSONALITY_ENGLISH, | |
| PERSONALITY_SPANISH, | |
| PERSONALITY_PORTUGUESE, | |
| PERSONALITY_ARABIC, | |
| PERSONALITY_FRENCH, | |
| PERSONALITY_CHINESE, | |
| PERSONALITY_BENGALI, | |
| PERSONALITY_SPANISH_GT, | |
| }; | |
| static_assert( | |
| ARRAYSIZE(globalAvailablePersonalities) == MSG_MAX - MSG_400, | |
| "Have you updated resource.h, localization_data.h and endless.loc?" | |
| ); | |
| // Rufus language codes | |
| #define RUFUS_LOCALE_EN "en-US" | |
| #define RUFUS_LOCALE_ES "es-ES" | |
| #define RUFUS_LOCALE_PT "pt-BR" | |
| #define RUFUS_LOCALE_SA "ar-SA" | |
| #define RUFUS_LOCALE_FR "fr-FR" | |
| #define RUFUS_LOCALE_ZH_CN "zh-CN" | |
| #define RUFUS_LOCALE_BN "bn-BD" | |
| // INI file language codes | |
| #define INI_LOCALE_EN "en_US.utf8" | |
| #define INI_LOCALE_ES "es_MX.utf8" | |
| #define INI_LOCALE_PT "pt_BR.utf8" | |
| #define INI_LOCALE_SA "ar_AE.utf8" | |
| #define INI_LOCALE_FR "fr_FR.utf8" | |
| #define INI_LOCALE_ZH "zh_CN.utf8" | |
| #define INI_LOCALE_BN "bn_BD.utf8" | |
| enum custom_message { | |
| WM_FILES_CHANGED = UM_NO_UPDATE + 1, | |
| WM_FINISHED_IMG_SCANNING, | |
| WM_UPDATE_PROGRESS, | |
| WM_FILE_DOWNLOAD_STATUS, | |
| WM_FINISHED_FILE_VERIFICATION, | |
| WM_FINISHED_FILE_COPY, | |
| WM_FINISHED_ALL_OPERATIONS, | |
| WM_INTERNET_CONNECTION_STATE, | |
| }; | |
| enum endless_action_type { | |
| OP_DOWNLOADING_FILES = OP_MAX, | |
| OP_VERIFYING_SIGNATURE, | |
| OP_FLASHING_DEVICE, | |
| OP_FILE_COPY, | |
| OP_SETUP_DUALBOOT, | |
| OP_NEW_LIVE_CREATION, | |
| OP_NO_OPERATION_IN_PROGRESS, | |
| OP_ENDLESS_MAX | |
| }; | |
| #define TID_UPDATE_FILES TID_REFRESH_TIMER + 1 | |
| #define ENABLE_JSON_COMPRESSION 1 | |
| #define BOOT_COMPONENTS_FOLDER "EndlessBoot" | |
| #define FILE_GZ_EXTENSION "gz" | |
| #define FILE_XZ_EXTENSION "xz" | |
| #define RELEASE_JSON_URLPATH _T("https://d1anzknqnc1kmb.cloudfront.net/") | |
| #define JSON_LIVE_FILE "releases-eos-3.json" | |
| #define JSON_INSTALLER_FILE "releases-eosinstaller-3.json" | |
| #define JSON_GZIP "." FILE_GZ_EXTENSION | |
| #define JSON_PACKED(__file__) __file__ JSON_GZIP | |
| #ifdef ENABLE_JSON_COMPRESSION | |
| #define JSON_URL(__file__) RELEASE_JSON_URLPATH _T(JSON_PACKED(__file__)) | |
| #else | |
| #define JSON_URL(__file__) RELEASE_JSON_URLPATH _T(__file__) | |
| #endif // ENABLE_JSON_COMPRESSION | |
| #define SIGNATURE_FILE_EXT L".asc" | |
| #define BOOT_ARCHIVE_SUFFIX L".boot.zip" | |
| #define IMAGE_FILE_EXT L".img" | |
| #define ENDLESS_OS "Endless OS" | |
| #define EOS_PRODUCT_TEXT "eos" | |
| #define EOS_INSTALLER_PRODUCT_TEXT "eosinstaller" | |
| #define EOS_NONFREE_PRODUCT_TEXT "eosnonfree" | |
| #define EOS_OEM_PRODUCT_TEXT "eosoem" | |
| #define EOS_RETAIL_PRODUCT_TEXT "eosretail" | |
| const wchar_t* mainWindowTitle = L"Endless Installer"; | |
| #define ALL_FILES L"*.*" | |
| #define ENDLESS_UNINSTALLER_NAME L"endless-uninstaller.exe" | |
| #define ENDLESS_OS_NAME L"Endless OS" | |
| #define ENDLESS_IMG_FILE_NAME L"endless.img" | |
| #define EXFAT_ENDLESS_LIVE_FILE_NAME L"live" | |
| // Radu: How much do we need to reserve for the exfat partition header? | |
| // reserve 10 mb for now; this will also include the signature file | |
| #define INSTALLER_DELTA_SIZE (10*1024*1024) | |
| #define BYTES_IN_MEGABYTE (1024 * 1024) | |
| #define BYTES_IN_GIGABYTE (1024 * 1024 * 1024) | |
| #define UPDATE_DOWNLOAD_PROGRESS_TIME 2000 | |
| #define CHECK_INTERNET_CONNECTION_TIME 2000 | |
| #define MIN_SUPPORTED_IE_VERSION 8 | |
| #define FORMAT_STATUS_CANCEL (ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_CANCELLED) | |
| static LPCTSTR OperationToStr(int op) | |
| { | |
| switch (op) | |
| { | |
| TOSTR(OP_ANALYZE_MBR); | |
| TOSTR(OP_BADBLOCKS); | |
| TOSTR(OP_ZERO_MBR); | |
| TOSTR(OP_PARTITION); | |
| TOSTR(OP_FORMAT); | |
| TOSTR(OP_CREATE_FS); | |
| TOSTR(OP_FIX_MBR); | |
| TOSTR(OP_DOS); | |
| TOSTR(OP_FINALIZE); | |
| TOSTR(OP_DOWNLOADING_FILES); | |
| TOSTR(OP_VERIFYING_SIGNATURE); | |
| TOSTR(OP_FLASHING_DEVICE); | |
| TOSTR(OP_FILE_COPY); | |
| TOSTR(OP_SETUP_DUALBOOT); | |
| TOSTR(OP_NEW_LIVE_CREATION); | |
| TOSTR(OP_NO_OPERATION_IN_PROGRESS); | |
| TOSTR(OP_ENDLESS_MAX); | |
| default: return _T("UNKNOWN_OPERATION"); | |
| } | |
| } | |
| static LPCTSTR ErrorCauseToStr(ErrorCause_t errorCause) | |
| { | |
| switch (errorCause) | |
| { | |
| TOSTR(ErrorCauseGeneric); | |
| TOSTR(ErrorCauseCancelled); | |
| TOSTR(ErrorCauseDownloadFailed); | |
| TOSTR(ErrorCauseDownloadFailedDiskFull); | |
| TOSTR(ErrorCauseVerificationFailed); | |
| TOSTR(ErrorCauseWriteFailed); | |
| TOSTR(ErrorCauseNot64Bit); | |
| TOSTR(ErrorCause32BitEFI); | |
| TOSTR(ErrorCauseBitLocker); | |
| TOSTR(ErrorCauseNotNTFS); | |
| TOSTR(ErrorCauseNonWindowsMBR); | |
| TOSTR(ErrorCauseNonEndlessMBR); | |
| TOSTR(ErrorCauseInstallFailedDiskFull); | |
| TOSTR(ErrorCauseSuspended); | |
| TOSTR(ErrorCauseNone); | |
| default: return _T("Error Cause Unknown"); | |
| } | |
| } | |
| static LPCTSTR InstallMethodToStr(InstallMethod_t installMethod) | |
| { | |
| switch (installMethod) | |
| { | |
| TOSTR(None); | |
| TOSTR(LiveUsb); | |
| TOSTR(ReformatterUsb); | |
| TOSTR(CombinedUsb); | |
| TOSTR(InstallDualBoot); | |
| TOSTR(UninstallDualBoot); | |
| default: return _T("Unknown"); | |
| } | |
| } | |
| extern "C" void UpdateProgress(int op, float percent) | |
| { | |
| static int oldPercent = 0; | |
| static int oldOp = -1; | |
| bool change = false; | |
| if (op != oldOp) { | |
| oldOp = op; | |
| oldPercent = (int)floor(percent); | |
| change = true; | |
| } else if(oldPercent != (int)floor(percent)) { | |
| oldPercent = (int)floor(percent); | |
| change = true; | |
| } | |
| if(change) PostMessage(hMainDialog, WM_UPDATE_PROGRESS, (WPARAM)op, (LPARAM)oldPercent); | |
| } | |
| // CEndlessUsbToolDlg dialog | |
| BEGIN_DHTML_EVENT_MAP(CEndlessUsbToolDlg) | |
| // For dragging the window | |
| DHTML_EVENT_CLASS(DISPID_HTMLELEMENTEVENTS_ONMOUSEDOWN, _T(CLASS_PAGE_HEADER_TITLE), OnHtmlMouseDown) | |
| DHTML_EVENT_CLASS(DISPID_HTMLELEMENTEVENTS_ONMOUSEDOWN, _T(CLASS_PAGE_HEADER), OnHtmlMouseDown) | |
| // Dual Boot Page handlers | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_LANGUAGE_DUALBOOT), OnLanguageChanged) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_DUALBOOT_CLOSE_BUTTON), OnCloseAppClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_DUALBOOT_ADVANCED_LINK), OnAdvancedOptionsClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_DUALBOOT_INSTALL_BUTTON), OnInstallDualBootClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_VERSION_LINK), OnLinkClicked) | |
| // Advanced Page Handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_LIVE_USB_BUTTON), OnLiveUsbClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_REFORMATTER_USB_BUTTON), OnReformatterUsbClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_COMPARE_OPTIONS), OnLinkClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_ADVANCED_CLOSE_BUTTON), OnCloseAppClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_ADVANCED_PREV_BUTTON), OnAdvancedPagePreviousClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_COMBINED_USB_BUTTON), OnCombinedUsbButtonClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_USB_LEARN_MORE), OnLinkClicked) | |
| // Select File Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELFILE_PREV_BUTTON), OnSelectFilePreviousClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELFILE_NEXT_BUTTON), OnSelectFileNextClicked) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_FILES_SELECT), OnSelectedImageFileChanged) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_REMOTE_SELECT), OnSelectedRemoteFileChanged) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_IMAGE_TYPE_LOCAL), OnSelectedImageTypeChanged) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_IMAGE_TYPE_REMOTE), OnSelectedImageTypeChanged) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_DOWNLOAD_LIGHT_BUTTON), OnDownloadLightButtonClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_DOWNLOAD_FULL_BUTTON), OnDownloadFullButtonClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_CONNECTED_LINK), OnLinkClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_CONNECTED_SUPPORT_LINK), OnLinkClicked) | |
| // Select USB Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELUSB_PREV_BUTTON), OnSelectUSBPreviousClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELUSB_NEXT_BUTTON), OnSelectUSBNextClicked) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_SELUSB_USB_DRIVES), OnSelectedUSBDiskChanged) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_SELUSB_AGREEMENT), OnAgreementCheckboxChanged) | |
| // Select Storage Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELSTORAGE_PREV_BUTTON), OnSelectStoragePreviousClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_SELSTORAGE_NEXT_BUTTON), OnSelectStorageNextClicked) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_STORAGE_SELECT), OnSelectedStorageSizeChanged) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_STORAGE_SUPPORT_LINK), OnLinkClicked) | |
| // Installing Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_INSTALL_CANCEL), OnInstallCancelClicked) | |
| // Thank You Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_CLOSE_BUTTON), OnCloseAppClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_USBBOOT_HOWTO), OnLinkClicked) | |
| // Error Page handlers | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_ERROR_CLOSE_BUTTON), OnCloseAppClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_ENDLESS_SUPPORT), OnLinkClicked) | |
| DHTML_EVENT_ONCLICK(_T(ELEMENT_ERROR_BUTTON), OnRecoverErrorButtonClicked) | |
| DHTML_EVENT_ONCHANGE(_T(ELEMENT_ERROR_DELETE_CHECKBOX), OnDeleteCheckboxChanged) | |
| END_DHTML_EVENT_MAP() | |
| BEGIN_DISPATCH_MAP(CEndlessUsbToolDlg, CDHtmlDialog) | |
| DISP_FUNCTION(CEndlessUsbToolDlg, "Debug", JavascriptDebug, VT_EMPTY, VTS_BSTR) | |
| END_DISPATCH_MAP() | |
| CMap<CString, LPCTSTR, uint32_t, uint32_t> CEndlessUsbToolDlg::m_personalityToLocaleMsg; | |
| CMap<CStringA, LPCSTR, CString, LPCTSTR> CEndlessUsbToolDlg::m_localeToPersonality; | |
| CMap<CStringA, LPCSTR, CStringA, LPCSTR> CEndlessUsbToolDlg::m_localeToIniLocale; | |
| int CEndlessUsbToolDlg::ImageUnpackOperation; | |
| int CEndlessUsbToolDlg::ImageUnpackPercentStart; | |
| int CEndlessUsbToolDlg::ImageUnpackPercentEnd; | |
| ULONGLONG CEndlessUsbToolDlg::ImageUnpackFileSize; | |
| const UINT CEndlessUsbToolDlg::m_uTaskbarBtnCreatedMsg = RegisterWindowMessage(_T("TaskbarButtonCreated")); | |
| CEndlessUsbToolDlg::CEndlessUsbToolDlg(UINT globalMessage, CWnd* pParent /*=NULL*/) | |
| : CDHtmlDialog(IDD_ENDLESSUSBTOOL_DIALOG, IDR_HTML_ENDLESSUSBTOOL_DIALOG, pParent), | |
| m_initialized(false), | |
| m_selectedLocale(NULL), | |
| m_localizationFile(""), | |
| m_shellNotificationsRegister(0), | |
| m_lastDevicesRefresh(0), | |
| m_spHtmlDoc3(NULL), | |
| m_lgpSet(FALSE), | |
| m_lgpExistingKey(FALSE), | |
| m_FilesChangedHandle(INVALID_HANDLE_VALUE), | |
| m_fileScanThread(INVALID_HANDLE_VALUE), | |
| m_operationThread(INVALID_HANDLE_VALUE), | |
| m_downloadUpdateThread(INVALID_HANDLE_VALUE), | |
| m_checkConnectionThread(INVALID_HANDLE_VALUE), | |
| m_spStatusElem(NULL), | |
| m_spWindowElem(NULL), | |
| m_dispexWindow(NULL), | |
| m_downloadManager(), | |
| m_useLocalFile(true), | |
| m_selectedRemoteIndex(-1), | |
| m_baseImageRemoteIndex(-1), | |
| m_usbDeleteAgreement(false), | |
| m_currentStep(OP_NO_OPERATION_IN_PROGRESS), | |
| m_cancelOperationEvent(CreateEvent(NULL, TRUE, FALSE, NULL)), | |
| m_closeFileScanThreadEvent(CreateEvent(NULL, TRUE, FALSE, NULL)), | |
| m_closeRequested(false), | |
| m_ieVersion(0), | |
| m_globalWndMessage(globalMessage), | |
| m_isConnected(false), | |
| m_lastErrorCause(ErrorCause_t::ErrorCauseNone), | |
| m_localFilesScanned(false), | |
| m_jsonDownloadState(JSONDownloadState::Pending), | |
| m_selectedInstallMethod(InstallMethod_t::None) | |
| { | |
| FUNCTION_ENTER; | |
| m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); | |
| size_t personalitiesCount = sizeof(globalAvailablePersonalities) / sizeof(globalAvailablePersonalities[0]); | |
| for (uint32_t index = 0; index < personalitiesCount; index++) { | |
| m_personalityToLocaleMsg.SetAt(globalAvailablePersonalities[index], MSG_400 + index); | |
| } | |
| m_localeToPersonality[RUFUS_LOCALE_EN] = PERSONALITY_ENGLISH; | |
| m_localeToPersonality[RUFUS_LOCALE_ES] = PERSONALITY_SPANISH; | |
| m_localeToPersonality[RUFUS_LOCALE_PT] = PERSONALITY_PORTUGUESE; | |
| m_localeToPersonality[RUFUS_LOCALE_SA] = PERSONALITY_ARABIC; | |
| m_localeToPersonality[RUFUS_LOCALE_FR] = PERSONALITY_FRENCH; | |
| m_localeToPersonality[RUFUS_LOCALE_ZH_CN] = PERSONALITY_CHINESE; | |
| m_localeToPersonality[RUFUS_LOCALE_BN] = PERSONALITY_BENGALI; | |
| m_localeToIniLocale[RUFUS_LOCALE_EN] = INI_LOCALE_EN; | |
| m_localeToIniLocale[RUFUS_LOCALE_ES] = INI_LOCALE_ES; | |
| m_localeToIniLocale[RUFUS_LOCALE_PT] = INI_LOCALE_PT; | |
| m_localeToIniLocale[RUFUS_LOCALE_SA] = INI_LOCALE_SA; | |
| m_localeToIniLocale[RUFUS_LOCALE_FR] = INI_LOCALE_FR; | |
| m_localeToIniLocale[RUFUS_LOCALE_ZH_CN] = INI_LOCALE_ZH; | |
| m_localeToIniLocale[RUFUS_LOCALE_BN] = INI_LOCALE_BN; | |
| } | |
| CEndlessUsbToolDlg::~CEndlessUsbToolDlg() { | |
| FUNCTION_ENTER; | |
| } | |
| void CEndlessUsbToolDlg::DoDataExchange(CDataExchange* pDX) | |
| { | |
| CDHtmlDialog::DoDataExchange(pDX); | |
| } | |
| BEGIN_MESSAGE_MAP(CEndlessUsbToolDlg, CDHtmlDialog) | |
| ON_WM_CLOSE() | |
| END_MESSAGE_MAP() | |
| const IID my_IID_ITaskbarList3 = | |
| { 0xea1afb91, 0x9e28, 0x4b86,{ 0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf } }; | |
| const IID my_CLSID_TaskbarList = | |
| { 0x56fdf344, 0xfd6d, 0x11d0,{ 0x95, 0x8a ,0x0, 0x60, 0x97, 0xc9, 0xa0 ,0x90 } }; | |
| LRESULT CEndlessUsbToolDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam) | |
| { | |
| if (nWindowsVersion >= WINDOWS_7) { | |
| m_taskbarProgress.Release(); | |
| //CoCreateInstance(my_CLSID_TaskbarList, NULL, CLSCTX_ALL, my_IID_ITaskbarList3, (void**)&m_taskbarProgress); | |
| HRESULT hr = m_taskbarProgress.CoCreateInstance(my_CLSID_TaskbarList); | |
| IFFALSE_GOTO(SUCCEEDED(hr), "Error creating progressbar.", done); | |
| } | |
| done: | |
| return 0; | |
| } | |
| // Browse navigation handling methods | |
| void CEndlessUsbToolDlg::OnDocumentComplete(LPDISPATCH pDisp, LPCTSTR szUrl) | |
| { | |
| FUNCTION_ENTER; | |
| CDHtmlDialog::OnDocumentComplete(pDisp, szUrl); | |
| uprintf("OnDocumentComplete '%ls'", szUrl); | |
| IFTRUE_RETURN(m_selectedInstallMethod == InstallMethod_t::UninstallDualBoot, "Returning as we are in uninstall mode"); | |
| if (this->m_spHtmlDoc == NULL) { | |
| uprintf("CEndlessUsbToolDlg::OnDocumentComplete m_spHtmlDoc==NULL"); | |
| return; | |
| } | |
| if (m_spHtmlDoc3 == NULL) { | |
| HRESULT hr = m_spHtmlDoc->QueryInterface(IID_IHTMLDocument3, (void**)&m_spHtmlDoc3); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && m_spHtmlDoc3 != NULL, "Error when querying IID_IHTMLDocument3 interface."); | |
| } | |
| AddLanguagesToUI(); | |
| ApplyRufusLocalization(); //apply_localization(IDD_ENDLESSUSBTOOL_DIALOG, GetSafeHwnd()); | |
| if (nWindowsVersion == WINDOWS_XP) { | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_REFORMATTER_USB_BUTTON))), CComVariant(FALSE)); | |
| } | |
| SetElementText(_T(ELEMENT_VERSION_LINK), CComBSTR(RELEASE_VER_STR)); | |
| StartCheckInternetConnectionThread(); | |
| FindMaxUSBSpeed(); | |
| UpdateDualBootTexts(); | |
| return; | |
| error: | |
| uprintf("OnDocumentComplete Exit with error"); | |
| } | |
| void CEndlessUsbToolDlg::InitRufus() | |
| { | |
| FUNCTION_ENTER; | |
| // RADU: try to remove the need for this | |
| hMainDialog = m_hWnd; | |
| hDeviceList = m_hWnd; | |
| hBootType = m_hWnd; | |
| hPartitionScheme = m_hWnd; | |
| hFileSystem = m_hWnd; | |
| hClusterSize = m_hWnd; | |
| hLabel = m_hWnd; | |
| hNBPasses = m_hWnd; | |
| hDiskID = m_hWnd; | |
| hInfo = m_hWnd; | |
| hMainInstance = AfxGetResourceHandle(); | |
| if (GetSystemDirectoryU(system_dir, sizeof(system_dir)) == 0) { | |
| uprintf("Could not get system directory: %s", WindowsErrorString()); | |
| safe_strcpy(system_dir, sizeof(system_dir), "C:\\Windows\\System32"); | |
| } | |
| // Construct Sysnative ourselves as there is no GetSysnativeDirectory() call | |
| // By default (64bit app running on 64 bit OS or 32 bit app running on 32 bit OS) | |
| // Sysnative and System32 are the same | |
| safe_strcpy(sysnative_dir, sizeof(sysnative_dir), system_dir); | |
| // But if the app is 32 bit and the OS is 64 bit, Sysnative must differ from System32 | |
| #if (!defined(_WIN64) && !defined(BUILD64)) | |
| if (is_x64()) { | |
| if (GetSystemWindowsDirectoryU(sysnative_dir, sizeof(sysnative_dir)) == 0) { | |
| uprintf("Could not get Windows directory: %s", WindowsErrorString()); | |
| safe_strcpy(sysnative_dir, sizeof(sysnative_dir), "C:\\Windows"); | |
| } | |
| safe_strcat(sysnative_dir, sizeof(sysnative_dir), "\\Sysnative"); | |
| } | |
| #endif | |
| // Initialize COM for folder selection | |
| IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); | |
| //// Some dialogs have Rich Edit controls and won't display without this | |
| //if (GetLibraryHandle("Riched20") == NULL) { | |
| // uprintf("Could not load RichEdit library - some dialogs may not display: %s\n", WindowsErrorString()); | |
| //} | |
| PF_INIT(GetTickCount64, kernel32); | |
| srand((unsigned int)_GetTickCount64()); | |
| } | |
| void CEndlessUsbToolDlg::ChangeDriveAutoRunAndMount(bool setEndlessValues) | |
| { | |
| if (setEndlessValues) { | |
| // We use local group policies rather than direct registry manipulation | |
| // 0x9e disables removable and fixed drive notifications | |
| m_lgpSet = SetLGP(FALSE, &m_lgpExistingKey, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", "NoDriveTypeAutorun", 0x9e); | |
| if (nWindowsVersion > WINDOWS_XP) { | |
| // Re-enable AutoMount if needed | |
| if (!GetAutoMount(&m_automount)) { | |
| uprintf("Could not get AutoMount status"); | |
| m_automount = TRUE; // So that we don't try to change its status on exit | |
| } | |
| else if (!m_automount) { | |
| uprintf("AutoMount was detected as disabled - temporary re-enabling it"); | |
| if (!SetAutoMount(TRUE)) { | |
| uprintf("Failed to enable AutoMount"); | |
| } | |
| } | |
| } | |
| } else { | |
| // revert settings we changed | |
| if (m_lgpSet) { | |
| SetLGP(TRUE, &m_lgpExistingKey, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", "NoDriveTypeAutorun", 0); | |
| } | |
| if ((nWindowsVersion > WINDOWS_XP) && (!m_automount) && (!SetAutoMount(FALSE))) { | |
| uprintf("Failed to restore AutoMount to disabled"); | |
| } | |
| } | |
| } | |
| // The scanning process can be blocking for message processing => use a thread | |
| DWORD WINAPI CEndlessUsbToolDlg::RufusISOScanThread(LPVOID param) | |
| { | |
| FUNCTION_ENTER; | |
| if (image_path == NULL) { | |
| uprintf("ERROR: image_path is NULL"); | |
| goto out; | |
| } | |
| PrintInfoDebug(0, MSG_202); | |
| img_report.is_iso = (BOOLEAN)ExtractISO(image_path, "", TRUE); | |
| img_report.is_bootable_img = (BOOLEAN)IsBootableImage(image_path); | |
| if (!img_report.is_iso && !img_report.is_bootable_img) { | |
| // Failed to scan image | |
| PrintInfoDebug(0, MSG_203); | |
| safe_free(image_path); | |
| PrintStatus(0, MSG_086); | |
| goto out; | |
| } | |
| if (img_report.is_bootable_img) { | |
| uprintf(" Image is a %sbootable %s image", | |
| (img_report.compression_type != BLED_COMPRESSION_NONE) ? "compressed " : "", img_report.is_vhd ? "VHD" : "disk"); | |
| } | |
| out: | |
| ::SendMessage(hMainDialog, WM_FINISHED_IMG_SCANNING, 0, 0); | |
| PrintInfo(0, MSG_210); | |
| ExitThread(0); | |
| } | |
| // CEndlessUsbToolDlg message handlers | |
| BOOL CEndlessUsbToolDlg::OnInitDialog() | |
| { | |
| FUNCTION_ENTER; | |
| CDHtmlDialog::OnInitDialog(); | |
| m_initialized = true; | |
| CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) }; | |
| PF_INIT(ChangeWindowMessageFilterEx, user32); | |
| if (nWindowsVersion >= WINDOWS_7 && pfChangeWindowMessageFilterEx != NULL) { | |
| pfChangeWindowMessageFilterEx(m_hWnd, m_uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, &cfs); | |
| } | |
| if(CEndlessUsbToolApp::m_enableLogDebugging) hLog = m_hWnd; | |
| // Set the icon for this dialog. The framework does this automatically | |
| // when the application's main window is not a dialog | |
| SetIcon(m_hIcon, TRUE); // Set big icon | |
| SetIcon(m_hIcon, FALSE); // Set small icon | |
| //refuse dragging file into this dialog | |
| m_pBrowserApp->put_RegisterAsDropTarget(VARIANT_FALSE); | |
| m_pBrowserApp->put_RegisterAsBrowser(FALSE); | |
| SetHostFlags(DOCHOSTUIFLAG_DIALOG | DOCHOSTUIFLAG_SCROLL_NO | DOCHOSTUIFLAG_NO3DBORDER); | |
| // Expose logging method to Javascript | |
| EnableAutomation(); | |
| LPDISPATCH pDisp = GetIDispatch(FALSE); | |
| SetExternalDispatch(pDisp); | |
| //Disable JavaScript errors | |
| m_pBrowserApp->put_Silent(VARIANT_TRUE); | |
| //// Remove caption and border | |
| SetWindowLong(m_hWnd, GWL_STYLE, GetWindowLong(m_hWnd, GWL_STYLE) | |
| & (~(WS_CAPTION | WS_BORDER))); | |
| // Move window | |
| SetWindowPos(NULL, 0, 0, 748, 514, SWP_NOMOVE | SWP_NOZORDER); | |
| GetIEVersion(); | |
| // Make round corners | |
| if(m_ieVersion >= 9) { // Internet explorer < 9 doesn't support rounded corners | |
| // Get the rectangle | |
| CRect rect; | |
| GetWindowRect(&rect); | |
| int w = rect.Width(); | |
| int h = rect.Height(); | |
| int radius = 11; | |
| CRgn rgnRound, rgnRect, rgnComp; | |
| rgnRound.CreateRoundRectRgn(0, 0, w + 1, h + radius, radius, radius); | |
| rgnRect.CreateRectRgn(0, 0, w, h); | |
| rgnComp.CreateRectRgn(0, 0, w, h); | |
| int combineResult = rgnComp.CombineRgn(&rgnRect, &rgnRound, RGN_AND); | |
| // Set the window region | |
| SetWindowRgn(static_cast<HRGN>(rgnComp.GetSafeHandle()), TRUE); | |
| } | |
| // Init localization before doing anything else | |
| LoadLocalizationData(); | |
| InitRufus(); | |
| // For Rufus | |
| CheckDlgButton(IDC_BOOT, BST_CHECKED); | |
| CheckDlgButton(IDC_QUICK_FORMAT, BST_CHECKED); | |
| bool result = m_downloadManager.Init(m_hWnd, WM_FILE_DOWNLOAD_STATUS); | |
| SetWindowTextW(L""); | |
| Analytics::instance()->startSession(); | |
| TrackEvent(_T("IEVersion"), m_ieVersion); | |
| if (m_ieVersion < MIN_SUPPORTED_IE_VERSION) { | |
| ShowIETooOldError(); | |
| EndDialog(IDCANCEL); | |
| } | |
| return TRUE; // return TRUE unless you set the focus to a control | |
| } | |
| void CEndlessUsbToolDlg::ShowIETooOldError() | |
| { | |
| CString message = UTF8ToCString(lmprintf(MSG_368, m_ieVersion, MIN_SUPPORTED_IE_VERSION)); | |
| int result = AfxMessageBox(message, MB_OKCANCEL | MB_ICONERROR); | |
| if (result == IDOK) { | |
| const char *command; | |
| if (nWindowsVersion >= WINDOWS_VISTA) { | |
| // https://msdn.microsoft.com/en-us/library/cc144191(VS.85).aspx | |
| command = "c:\\windows\\system32\\control.exe /name Microsoft.WindowsUpdate"; | |
| } else { | |
| // This is what the Windows Update item in the Start menu points to | |
| command = "c:\\windows\\system32\\wupdmgr.exe"; | |
| } | |
| UINT ret = WinExec(command, SW_NORMAL); | |
| if (ret <= 31) { | |
| uprintf("WinExec('%s', SW_NORMAL) failed: %d", command, ret); | |
| } | |
| } | |
| } | |
| #define THREADS_WAIT_TIMEOUT 10000 // 10 seconds | |
| void CEndlessUsbToolDlg::Uninit() | |
| { | |
| FUNCTION_ENTER; | |
| IFFALSE_RETURN(m_initialized, "not yet initialized; or already uninitialized"); | |
| m_initialized = false; | |
| int handlesCount = 0; | |
| HANDLE handlesToWaitFor[5]; | |
| HANDLE analyticsHandle = Analytics::instance()->stopSession(InstallMethodToStr(m_selectedInstallMethod)); | |
| #define await_handle(h) do { \ | |
| if (h != INVALID_HANDLE_VALUE) handlesToWaitFor[handlesCount++] = h; \ | |
| } while (0) | |
| await_handle(analyticsHandle); | |
| await_handle(m_fileScanThread); | |
| await_handle(m_operationThread); | |
| await_handle(m_downloadUpdateThread); | |
| await_handle(m_checkConnectionThread); | |
| #undef await_handle | |
| if (handlesCount > 0) { | |
| if (m_closeFileScanThreadEvent != INVALID_HANDLE_VALUE) SetEvent(m_closeFileScanThreadEvent); | |
| if (m_cancelOperationEvent != INVALID_HANDLE_VALUE) SetEvent(m_cancelOperationEvent); | |
| DWORD waitStatus = WaitForMultipleObjects(handlesCount, handlesToWaitFor, TRUE, THREADS_WAIT_TIMEOUT); | |
| if (waitStatus == WAIT_TIMEOUT) { | |
| uprintf("Error: waited for %d millis for threads to finish.", THREADS_WAIT_TIMEOUT); | |
| } | |
| } | |
| if (m_cancelOperationEvent != INVALID_HANDLE_VALUE) { | |
| CloseHandle(m_cancelOperationEvent); | |
| m_cancelOperationEvent = INVALID_HANDLE_VALUE; | |
| } | |
| if (m_closeFileScanThreadEvent != INVALID_HANDLE_VALUE) { | |
| CloseHandle(m_closeFileScanThreadEvent); | |
| m_closeFileScanThreadEvent = INVALID_HANDLE_VALUE; | |
| } | |
| m_downloadManager.Uninit(); | |
| // unregister from notifications and delete drive list related memory | |
| LeavingDevicesPage(); | |
| StrArrayDestroy(&DriveID); | |
| StrArrayDestroy(&DriveLabel); | |
| // delete image file entries related memory | |
| CString currentPath; | |
| pFileImageEntry_t currentEntry = NULL; | |
| for (POSITION position = m_imageFiles.GetStartPosition(); position != NULL; ) { | |
| m_imageFiles.GetNextAssoc(position, currentPath, currentEntry); | |
| delete currentEntry; | |
| } | |
| m_imageFiles.RemoveAll(); | |
| // uninit localization memory | |
| exit_localization(); | |
| safe_free(image_path); | |
| CLOSE_OPENED_LIBRARIES; | |
| } | |
| // If you add a minimize button to your dialog, you will need the code below | |
| // to draw the icon. For MFC applications using the document/view model, | |
| // this is automatically done for you by the framework. | |
| void CEndlessUsbToolDlg::OnPaint() | |
| { | |
| if (IsIconic()) | |
| { | |
| CPaintDC dc(this); // device context for painting | |
| SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); | |
| // Center icon in client rectangle | |
| int cxIcon = GetSystemMetrics(SM_CXICON); | |
| int cyIcon = GetSystemMetrics(SM_CYICON); | |
| CRect rect; | |
| GetClientRect(&rect); | |
| int x = (rect.Width() - cxIcon + 1) / 2; | |
| int y = (rect.Height() - cyIcon + 1) / 2; | |
| // Draw the icon | |
| dc.DrawIcon(x, y, m_hIcon); | |
| } | |
| else | |
| { | |
| CDHtmlDialog::OnPaint(); | |
| } | |
| } | |
| // The system calls this function to obtain the cursor to display while the user drags | |
| // the minimized window. | |
| HCURSOR CEndlessUsbToolDlg::OnQueryDragIcon() | |
| { | |
| return static_cast<HCURSOR>(m_hIcon); | |
| } | |
| BOOL CEndlessUsbToolDlg::PreTranslateMessage(MSG* pMsg) | |
| { | |
| // disable any key | |
| if (pMsg->message == WM_KEYDOWN) { | |
| return TRUE; | |
| } else if(pMsg->message == WM_MOUSEWHEEL && (GetKeyState(VK_CONTROL) & 0x8000)) { // Scroll With Ctrl + Mouse Wheel | |
| return TRUE; | |
| } | |
| return CDHtmlDialog::PreTranslateMessage(pMsg); | |
| } | |
| void CEndlessUsbToolDlg::JavascriptDebug(LPCTSTR debugMsg) | |
| { | |
| CComBSTR msg = debugMsg; | |
| uprintf("Javascript - [%ls]", msg); | |
| } | |
| /* | |
| * Device Refresh Timer | |
| */ | |
| void CALLBACK CEndlessUsbToolDlg::RefreshTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) | |
| { | |
| switch (idEvent) { | |
| case TID_REFRESH_TIMER: | |
| // DO NOT USE WM_DEVICECHANGE - IT MAY BE FILTERED OUT BY WINDOWS! | |
| ::PostMessage(hWnd, UM_MEDIA_CHANGE, 0, 0); | |
| break; | |
| case TID_UPDATE_FILES: | |
| ::PostMessage(hWnd, WM_FILES_CHANGED, 0, 0); | |
| break; | |
| default: | |
| uprintf("Timer not handled [%d]", idEvent); | |
| break; | |
| } | |
| } | |
| LRESULT CEndlessUsbToolDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) | |
| { | |
| if (message == m_uTaskbarBtnCreatedMsg) { | |
| OnTaskbarBtnCreated(wParam, lParam); | |
| } else if (message >= CB_GETEDITSEL && message < CB_MSGMAX) { | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| CComPtr<IHTMLOptionElement> optionElement; | |
| HRESULT hr; | |
| hr = GetSelectElement(_T(ELEMENT_SELUSB_USB_DRIVES), selectElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error returned from GetSelectElement."); | |
| switch (message) { | |
| case CB_RESETCONTENT: | |
| { | |
| hr = selectElement->put_length(0); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error clearing elements from USB drive list."); | |
| break; | |
| } | |
| case CB_ADDSTRING: | |
| { | |
| CComBSTR text = (wchar_t*)lParam; | |
| long index = 0; | |
| hr = AddEntryToSelect(selectElement, text, text, &index); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error adding item in USB drive list."); | |
| return index; | |
| } | |
| case CB_SETCURSEL: | |
| case CB_SETITEMDATA: | |
| case CB_GETITEMDATA: | |
| { | |
| CComPtr<IDispatch> pDispatch; | |
| CComBSTR valueBSTR; | |
| long index = (long)wParam; | |
| long value = (long)lParam; | |
| hr = selectElement->item(CComVariant(index), CComVariant(0), &pDispatch); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && pDispatch != NULL, "Error when querying for element at requested index."); | |
| hr = pDispatch.QueryInterface<IHTMLOptionElement>(&optionElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error when querying for IHTMLOptionElement interface"); | |
| if (message == CB_SETITEMDATA) { | |
| hr = ::VarBstrFromI4(value, LOCALE_SYSTEM_DEFAULT, 0, &valueBSTR); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error converting int value to BSTR."); | |
| hr = optionElement->put_value(valueBSTR); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error setting value for requested element."); | |
| } | |
| else if (message == CB_GETITEMDATA) { | |
| hr = optionElement->get_value(&valueBSTR); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error getting value for requested element."); | |
| hr = ::VarI4FromStr(valueBSTR, LOCALE_SYSTEM_DEFAULT, 0, &value); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error converting BSTR to int value."); | |
| return value; | |
| } | |
| else if (message == CB_SETCURSEL) { | |
| hr = optionElement->put_selected(VARIANT_TRUE); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error setting selected for requested element."); | |
| } | |
| break; | |
| } | |
| case CB_GETCURSEL: | |
| { | |
| long index = -1; | |
| hr = selectElement->get_selectedIndex(&index); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error querying for selected index."); | |
| return index; | |
| } | |
| case CB_GETCOUNT: | |
| { | |
| long count = 0; | |
| hr = selectElement->get_length(&count); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error querying for list count."); | |
| return count; | |
| } | |
| // Ignore | |
| case CB_SETDROPPEDWIDTH: | |
| break; | |
| default: | |
| luprintf("Untreated CB message %d(0x%X)", message, message); | |
| break; | |
| } | |
| return 0; | |
| error: | |
| uprintf("CEndlessUsbToolDlg::WindowProc called with %d[0x%X] (%d, %d); FAILURE %x", message, message, lParam, wParam, hr); | |
| return -1; | |
| } else if (message >= EM_GETSEL && message < EM_GETIMESTATUS) { | |
| // Handle messages sent to the log window | |
| // Do not use any method that calls _uprintf as it will generate an infinite recursive loop | |
| // Use OutputDebugString instead. | |
| switch (message) { | |
| case EM_SETSEL: | |
| { | |
| char *logMessage = (char*)lParam; | |
| CEndlessUsbToolApp::Log(logMessage); | |
| free(logMessage); | |
| break; | |
| } | |
| } | |
| } else { | |
| switch (message) { | |
| case WM_SETTEXT: | |
| { | |
| lParam = (LPARAM)mainWindowTitle; | |
| break; | |
| } | |
| case WM_CLIENTSHUTDOWN: | |
| case WM_QUERYENDSESSION: | |
| case WM_ENDSESSION: | |
| // TODO: Do we want to use ShutdownBlockReasonCreate() in Vista and later to stop | |
| // forced shutdown? See https://msdn.microsoft.com/en-us/library/ms700677.aspx | |
| if (m_operationThread != INVALID_HANDLE_VALUE) { | |
| // WM_QUERYENDSESSION uses this value to prevent shutdown | |
| return (INT_PTR)TRUE; | |
| } | |
| PostMessage(WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0); | |
| break; | |
| case UM_MEDIA_CHANGE: | |
| wParam = DBT_CUSTOMEVENT; | |
| // Fall through | |
| case WM_DEVICECHANGE: | |
| // The Windows hotplug subsystem sucks. Among other things, if you insert a GPT partitioned | |
| // USB drive with zero partitions, the only device messages you will get are a stream of | |
| // DBT_DEVNODES_CHANGED and that's it. But those messages are also issued when you get a | |
| // DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE, and there's a whole slew of them so we | |
| // can't really issue a refresh for each one we receive | |
| // What we do then is arm a timer on DBT_DEVNODES_CHANGED, if it's been more than 1 second | |
| // since last refresh/arm timer, and have that timer send DBT_CUSTOMEVENT when it expires. | |
| // DO *NOT* USE WM_DEVICECHANGE AS THE MESSAGE FROM THE TIMER PROC, as it may be filtered! | |
| // For instance filtering will occur when (un)plugging in a FreeBSD UFD on Windows 8. | |
| // Instead, use a custom user message, such as UM_MEDIA_CHANGE, to set DBT_CUSTOMEVENT. | |
| // RADU: check to see if this happens durring download/verification phases and the selected disk dissapeared | |
| // we can still ask the user to select another disk after download/verification has finished | |
| if (m_operationThread == INVALID_HANDLE_VALUE) { | |
| switch (wParam) { | |
| case DBT_DEVICEARRIVAL: | |
| case DBT_DEVICEREMOVECOMPLETE: | |
| case DBT_CUSTOMEVENT: // Sent by our timer refresh function or for card reader media change | |
| m_lastDevicesRefresh = _GetTickCount64(); | |
| KillTimer(TID_REFRESH_TIMER); | |
| GetUSBDevices((DWORD)ComboBox_GetItemData(hDeviceList, ComboBox_GetCurSel(hDeviceList))); | |
| OnSelectedUSBDiskChanged(NULL); | |
| return (INT_PTR)TRUE; | |
| case DBT_DEVNODES_CHANGED: | |
| // If it's been more than a second since last device refresh, arm a refresh timer | |
| if (_GetTickCount64() > m_lastDevicesRefresh + 1000) { | |
| m_lastDevicesRefresh = _GetTickCount64(); | |
| SetTimer(TID_REFRESH_TIMER, 1000, RefreshTimer); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| break; | |
| case UM_PROGRESS_INIT: | |
| { | |
| uprintf("Started to scan the provided image."); | |
| break; | |
| } | |
| case WM_FINISHED_IMG_SCANNING: | |
| { | |
| uprintf("Image scanning done."); | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| if (!img_report.is_bootable_img) { | |
| uprintf("FAILURE: selected image is not bootable"); | |
| ErrorOccured(ErrorCause_t::ErrorCauseVerificationFailed); | |
| } else { | |
| uprintf("Bootable image selected with correct format."); | |
| // Start formatting | |
| FormatStatus = 0; | |
| int nDeviceIndex = ComboBox_GetCurSel(hDeviceList); | |
| if (nDeviceIndex != CB_ERR) { | |
| DWORD DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex); | |
| StartOperationThread(OP_FLASHING_DEVICE, FormatThread, (LPVOID)(uintptr_t)DeviceNum); | |
| } | |
| } | |
| break; | |
| } | |
| case WM_UPDATE_PROGRESS: | |
| { | |
| HRESULT hr; | |
| int op = (int)wParam; | |
| int percent = (int)lParam; | |
| uprintf("Operation %ls(%d) with progress %d", OperationToStr(op), op, percent); | |
| // Ignore exFAT format progress. | |
| if ((m_currentStep == OP_FILE_COPY || m_currentStep == OP_NEW_LIVE_CREATION) && (op == OP_FORMAT || op == OP_CREATE_FS)) break; | |
| // Radu: pass all the files to be verified to verfication thread and do the percent calculation there | |
| // Not very happy about this but eh, needs refactoring | |
| if (op == OP_VERIFYING_SIGNATURE) { | |
| if (IsDualBootOrCombinedUsb()) { | |
| // Radu: do we need to do anything special here? | |
| // Does it make sense to add the boot archive signature verification to the percentage calculation also? | |
| // We are talking about 6 MB compared to more than 2 GB | |
| } else { | |
| if (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| ULONGLONG totalSize = m_selectedFileSize + m_localInstallerImage.size; | |
| ULONGLONG currentSize = 0; | |
| bool isInstallerImage = (m_localFile == m_localInstallerImage.filePath); | |
| if (isInstallerImage) { | |
| currentSize = m_selectedFileSize + (m_localInstallerImage.size * percent / 100); | |
| } | |
| else { | |
| currentSize = m_selectedFileSize * percent / 100; | |
| } | |
| percent = (int)(currentSize * 100 / totalSize); | |
| } | |
| } | |
| } | |
| // Radu: maybe divide the progress bar also based on the size of the image to be copied to disk after format is complete | |
| if (op == OP_FORMAT && m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| percent = percent / 2; | |
| } else if (op == OP_FILE_COPY) { | |
| percent = 50 + percent / 2; | |
| } | |
| if (percent >= 0 && percent <= 100) { | |
| hr = CallJavascript(_T(JS_SET_PROGRESS), CComVariant(percent)); | |
| IFFALSE_BREAK(SUCCEEDED(hr), "Error when calling set progress."); | |
| if (m_taskbarProgress != NULL) { | |
| m_taskbarProgress->SetProgressState(m_hWnd, TBPF_NORMAL); | |
| m_taskbarProgress->SetProgressValue(m_hWnd, percent, 100); | |
| } | |
| } | |
| if (op == OP_VERIFYING_SIGNATURE || op == OP_FORMAT || op == OP_FILE_COPY || op == OP_SETUP_DUALBOOT || op == OP_NEW_LIVE_CREATION) { | |
| CString downloadString; | |
| downloadString.Format(L"%d%%", percent); | |
| SetElementText(_T(ELEMENT_INSTALL_STATUS), CComBSTR(downloadString)); | |
| } | |
| break; | |
| } | |
| case UM_PROGRESS_EXIT: | |
| case UM_NO_UPDATE: | |
| luprintf("Untreated Rufus UM message %d(0x%X)", message, message); | |
| break; | |
| case UM_FORMAT_COMPLETED: | |
| { | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| if (!IS_ERROR(FormatStatus) && m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| StartOperationThread(OP_FILE_COPY, CEndlessUsbToolDlg::FileCopyThread); | |
| } else { | |
| if (IS_ERROR(FormatStatus) && m_lastErrorCause == ErrorCause_t::ErrorCauseNone) { | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseWriteFailed; | |
| } | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| } | |
| break; | |
| } | |
| case WM_FILES_CHANGED: | |
| { | |
| UpdateFileEntries(); | |
| break; | |
| } | |
| case WM_FILE_DOWNLOAD_STATUS: | |
| { | |
| DownloadStatus_t *downloadStatus = (DownloadStatus_t *)wParam; | |
| IFFALSE_BREAK(downloadStatus != NULL, "downloadStatus is NULL"); | |
| bool isReleaseJsonDownload = downloadStatus->jobName == DownloadManager::GetJobName(DownloadType_t::DownloadTypeReleseJson); | |
| // DO STUFF | |
| if (downloadStatus->error) { | |
| if (isReleaseJsonDownload) { | |
| uprintf("JSON download failed."); | |
| SetJSONDownloadState(JSONDownloadState::Failed); | |
| } else if (m_lastErrorCause == ErrorCause_t::ErrorCauseCancelled) { | |
| uprintf("Download cancelled by user request."); | |
| } else { | |
| uprintf("Error on download."); | |
| bool diskFullError = (downloadStatus->errorContext == BG_ERROR_CONTEXT_LOCAL_FILE); | |
| diskFullError = diskFullError && (downloadStatus->errorCode == HRESULT_FROM_WIN32(ERROR_DISK_FULL)); | |
| m_lastErrorCause = diskFullError ? ErrorCause_t::ErrorCauseDownloadFailedDiskFull : ErrorCause_t::ErrorCauseDownloadFailed; | |
| m_downloadManager.ClearExtraDownloadJobs(); | |
| ErrorOccured(m_lastErrorCause); | |
| } | |
| } else if (downloadStatus->done) { | |
| uprintf("Download done for %ls", downloadStatus->jobName); | |
| if (isReleaseJsonDownload) { | |
| if (m_jsonDownloadState != JSONDownloadState::Succeeded) { | |
| UpdateDownloadOptions(); | |
| } | |
| } else { | |
| StartOperationThread(OP_VERIFYING_SIGNATURE, CEndlessUsbToolDlg::FileVerificationThread); | |
| } | |
| } else { | |
| CStringA part, total; | |
| part = SizeToHumanReadable(downloadStatus->progress.BytesTransferred, FALSE, use_fake_units); | |
| total = SizeToHumanReadable(downloadStatus->progress.BytesTotal, FALSE, use_fake_units); | |
| uprintf("Download [%ls] progress %s of %s (%d of %d files) %s", downloadStatus->jobName, | |
| part, total, | |
| downloadStatus->progress.FilesTransferred, downloadStatus->progress.FilesTotal, | |
| downloadStatus->transientError ? "(transient error)" : ""); | |
| if (isReleaseJsonDownload) { | |
| if (downloadStatus->transientError) { | |
| SetJSONDownloadState(JSONDownloadState::Retrying); | |
| } | |
| // Don't clear this on the downwards edge. If the link is up but the connection times out, the | |
| // download will enter state TRANSIENT_ERROR and then immediately re-enter state CONNECTING. | |
| // We want to give some indication that some error has occured, even as we retry. | |
| } else { | |
| static ULONGLONG startedTickCount = 0; | |
| static ULONGLONG startedBytes = 0; | |
| RemoteImageEntry_t remote = m_remoteImages.GetAt(m_remoteImages.FindIndex(m_selectedRemoteIndex)); | |
| // we don't take the signature files into account but we are taking about ~2KB compared to >2GB | |
| ULONGLONG totalSize = downloadStatus->progress.BytesTotal; //GetActualDownloadSize(remote); | |
| ULONGLONG percent = downloadStatus->progress.BytesTransferred * 100 / totalSize; | |
| PostMessage(WM_UPDATE_PROGRESS, (WPARAM)OP_DOWNLOADING_FILES, (LPARAM)percent); | |
| // calculate speed | |
| CStringA speed("---"); | |
| ULONGLONG currentTickCount = _GetTickCount64(); | |
| if (startedTickCount != 0) { | |
| ULONGLONG diffBytes = downloadStatus->progress.BytesTransferred - startedBytes; | |
| ULONGLONG diffMillis = currentTickCount - startedTickCount; | |
| double diffSeconds = diffMillis / 1000.0; | |
| speed = SizeToHumanReadable((ULONGLONG) (diffBytes / diffSeconds), FALSE, use_fake_units); | |
| } else { | |
| startedTickCount = currentTickCount; | |
| startedBytes = downloadStatus->progress.BytesTransferred; | |
| } | |
| CStringA strDownloaded = SizeToHumanReadable(downloadStatus->progress.BytesTransferred, FALSE, use_fake_units); | |
| CStringA strTotal = totalSize == BG_SIZE_UNKNOWN ? "---" : SizeToHumanReadable(totalSize, FALSE, use_fake_units); | |
| // push to UI | |
| CString downloadString = UTF8ToCString(lmprintf(MSG_302, strDownloaded, strTotal, speed)); | |
| SetElementText(_T(ELEMENT_INSTALL_STATUS), CComBSTR(downloadString)); | |
| } | |
| } | |
| delete downloadStatus; | |
| break; | |
| } | |
| case WM_FINISHED_FILE_VERIFICATION: | |
| { | |
| BOOL result = (BOOL)wParam; | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| if (result) { | |
| uprintf("Verification passed."); | |
| bool verifiedInstallerImage = (m_localFile == m_localInstallerImage.filePath); | |
| bool verifiedBootFilesZip = (m_localFile == m_bootArchive); | |
| if (IsDualBootOrCombinedUsb()) { | |
| if (verifiedBootFilesZip) { | |
| // we checked the boot archive signature, now check the image | |
| m_localFile = UTF8ToCString(image_path); | |
| GetSignatureForLocalFile(m_localFile, m_localFileSig); | |
| StartOperationThread(OP_VERIFYING_SIGNATURE, CEndlessUsbToolDlg::FileVerificationThread); | |
| } else { | |
| m_cancelImageUnpack = 0; | |
| if (m_selectedInstallMethod == InstallMethod_t::CombinedUsb) { | |
| StartOperationThread(OP_NEW_LIVE_CREATION, CEndlessUsbToolDlg::CreateUSBStick); | |
| } else { | |
| StartOperationThread(OP_SETUP_DUALBOOT, CEndlessUsbToolDlg::SetupDualBoot); | |
| } | |
| } | |
| } else if (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb && !verifiedInstallerImage) { | |
| safe_free(image_path); | |
| image_path = wchar_to_utf8(m_localInstallerImage.filePath); | |
| // Radu: please make time to refactor this. | |
| // store this to copy them | |
| m_LiveFile = m_localFile; | |
| m_LiveFileSig = m_localFileSig; | |
| m_localFile = UTF8ToCString(image_path); | |
| m_localFileSig = m_localFile + SIGNATURE_FILE_EXT; | |
| StartOperationThread(OP_VERIFYING_SIGNATURE, CEndlessUsbToolDlg::FileVerificationThread); | |
| } else { | |
| StartOperationThread(OP_FLASHING_DEVICE, CEndlessUsbToolDlg::RufusISOScanThread); | |
| } | |
| } | |
| else { | |
| uprintf("Signature verification failed."); | |
| m_currentStep = OP_NO_OPERATION_IN_PROGRESS; | |
| if (m_lastErrorCause == ErrorCause_t::ErrorCauseNone) { | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseVerificationFailed; | |
| ErrorOccured(m_lastErrorCause); | |
| } else { | |
| ErrorOccured(m_lastErrorCause); | |
| } | |
| } | |
| break; | |
| } | |
| case WM_FINISHED_FILE_COPY: | |
| { | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| break; | |
| } | |
| case WM_FINISHED_ALL_OPERATIONS: | |
| { | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| m_currentStep = OP_NO_OPERATION_IN_PROGRESS; | |
| EnableHibernate(); | |
| if (m_selectedInstallMethod != InstallMethod_t::InstallDualBoot) ChangeDriveAutoRunAndMount(false); | |
| switch (m_lastErrorCause) { | |
| case ErrorCause_t::ErrorCauseNone: | |
| TrackEvent(_T("Completed")); | |
| PrintInfo(0, MSG_210); | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| if (m_taskbarProgress != NULL) { | |
| m_taskbarProgress->SetProgressState(m_hWnd, TBPF_NOPROGRESS); | |
| m_taskbarProgress->SetProgressValue(m_hWnd, 0, 100); | |
| } | |
| ChangePage(_T(ELEMENT_SUCCESS_PAGE)); | |
| break; | |
| default: | |
| ErrorOccured(m_lastErrorCause); | |
| break; | |
| } | |
| FormatStatus = 0; | |
| break; | |
| } | |
| case WM_INTERNET_CONNECTION_STATE: | |
| { | |
| bool connected = ((BOOL)wParam) == TRUE; | |
| m_isConnected = connected; | |
| if (m_isConnected) { | |
| StartJSONDownload(); | |
| } | |
| UpdateDownloadableState(); | |
| break; | |
| } | |
| case WM_POWERBROADCAST: | |
| { | |
| uprintf("Received WM_POWERBROADCAST with WPARAM 0x%X LPARAM 0x%X", wParam, lParam); | |
| if (m_currentStep == OP_NO_OPERATION_IN_PROGRESS) { | |
| uprintf("no operation in progress, ignoring"); | |
| return TRUE; | |
| } | |
| switch (wParam) { | |
| case PBT_APMQUERYSUSPEND: | |
| // Windows XP only. Vista and later do not send PBT_APMQUERYSUSPEND; | |
| // we attempt to inhibit suspend in EnableHibernate with the newer API. | |
| uprintf("Received WM_POWERBROADCAST with PBT_APMQUERYSUSPEND and trying to cancel it."); | |
| return BROADCAST_QUERY_DENY; | |
| case PBT_APMSUSPEND: | |
| uprintf("Received PBT_APMSUSPEND so canceling the operation."); | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseSuspended; | |
| CancelRunningOperation(); | |
| break; | |
| } | |
| return TRUE; | |
| } | |
| default: | |
| if (m_globalWndMessage == message) { | |
| SetForegroundWindow(); | |
| } | |
| //luprintf("Untreated message %d(0x%X)", message, message); | |
| break; | |
| } | |
| } | |
| return CDHtmlDialog::WindowProc(message, wParam, lParam); | |
| } | |
| // Disable context menu | |
| STDMETHODIMP CEndlessUsbToolDlg::ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved) | |
| { | |
| FUNCTION_ENTER; | |
| return S_OK; | |
| } | |
| // prevent refresh | |
| STDMETHODIMP CEndlessUsbToolDlg::TranslateAccelerator(LPMSG lpMsg, const GUID * pguidCmdGroup, DWORD nCmdID) | |
| { | |
| if (lpMsg && lpMsg->message == WM_KEYDOWN && | |
| (lpMsg->wParam == VK_F5 || | |
| lpMsg->wParam == VK_CONTROL)) | |
| { | |
| return S_OK; | |
| } | |
| return CDHtmlDialog::TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID); | |
| } | |
| // Drag window | |
| HRESULT CEndlessUsbToolDlg::OnHtmlMouseDown(IHTMLElement* pElement) | |
| { | |
| POINT pt; | |
| GetCursorPos(&pt); | |
| SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pt.x, pt.y)); | |
| return S_OK; | |
| } | |
| // Private methods | |
| void CEndlessUsbToolDlg::LoadLocalizationData() | |
| { | |
| FUNCTION_ENTER; | |
| const char* rufus_loc = "endless.loc"; | |
| int lcid = GetUserDefaultUILanguage(); | |
| BYTE *loc_data; | |
| DWORD loc_size, size; | |
| char tmp_path[MAX_PATH] = "", loc_file[MAX_PATH] = ""; | |
| HANDLE hFile = NULL; | |
| init_localization(); | |
| loc_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_LC_ENDLESS_LOC), _RT_RCDATA, "embedded.loc", &loc_size, FALSE); | |
| if ((GetTempPathU(sizeof(tmp_path), tmp_path) == 0) | |
| || (GetTempFileNameU(tmp_path, APPLICATION_NAME, 0, m_localizationFile) == 0) | |
| || (m_localizationFile[0] == 0)) { | |
| // Last ditch effort to get a loc file - just extract it to the current directory | |
| safe_strcpy(m_localizationFile, sizeof(m_localizationFile), rufus_loc); | |
| } | |
| hFile = CreateFileU(m_localizationFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | |
| if ((hFile == INVALID_HANDLE_VALUE) || (!WriteFileWithRetry(hFile, loc_data, loc_size, &size, WRITE_RETRIES))) { | |
| uprintf("localization: unable to extract '%s': %s", m_localizationFile, WindowsErrorString()); | |
| safe_closehandle(hFile); | |
| goto error; | |
| } | |
| uprintf("localization: extracted data to '%s'", m_localizationFile); | |
| safe_closehandle(hFile); | |
| if ((!get_supported_locales(m_localizationFile)) | |
| || ((m_selectedLocale = get_locale_from_lcid(lcid, TRUE)) == NULL)) { | |
| uprintf("FATAL: Could not access locale!"); | |
| goto error; | |
| } | |
| selected_langid = get_language_id(m_selectedLocale); | |
| if (get_loc_data_file(m_localizationFile, m_selectedLocale)) | |
| uprintf("Save locale to settings?"); | |
| return; | |
| error: | |
| uprintf("Exiting with error"); | |
| MessageBoxU(NULL, "The locale data is missing or invalid. This application will now exit.", "Fatal error", MB_ICONSTOP | MB_SYSTEMMODAL); | |
| } | |
| void CEndlessUsbToolDlg::ApplyRufusLocalization() | |
| { | |
| FUNCTION_ENTER; | |
| loc_cmd* lcmd = NULL; | |
| int dlg_id = IDD_ENDLESSUSBTOOL_DIALOG; | |
| HWND hCtrl = NULL; | |
| HWND hDlg = GetSafeHwnd(); | |
| if (this->m_spHtmlDoc == NULL) { | |
| uprintf("CEndlessUsbToolDlg::ApplyRufusLocalization m_spHtmlDoc==NULL"); | |
| return; | |
| } | |
| HRESULT hr; | |
| CComPtr<IHTMLElement> pElement = NULL; | |
| // go through the ids and update the text | |
| list_for_each_entry(lcmd, &loc_dlg[dlg_id - IDD_DIALOG].list, loc_cmd, list) { | |
| if (lcmd->command <= LC_TEXT) { | |
| if (lcmd->ctrl_id == dlg_id) { | |
| luprint("Updating dialog title not implemented"); | |
| } else { | |
| pElement = NULL; | |
| hr = m_spHtmlDoc3->getElementById(CComBSTR(lcmd->txt[0]), &pElement); | |
| if (FAILED(hr) || pElement == NULL) { | |
| luprintf("control '%s' is not part of dialog '%s'\n", lcmd->txt[0], control_id[dlg_id - IDD_DIALOG].name); | |
| } | |
| } | |
| } | |
| switch (lcmd->command) { | |
| case LC_TEXT: | |
| if (pElement != NULL) { | |
| if ((lcmd->txt[1] != NULL) && (lcmd->txt[1][0] != 0)) { | |
| hr = pElement->put_innerHTML(UTF8ToBSTR(lcmd->txt[1])); | |
| if (FAILED(hr)) { | |
| luprintf("error when updating control '%s', hr='%x'\n", lcmd->txt[0], hr); | |
| } | |
| } | |
| } | |
| break; | |
| case LC_MOVE: | |
| luprint("LC_MOVE not implemented"); | |
| break; | |
| case LC_SIZE: | |
| luprint("LC_SIZE not implemented"); | |
| break; | |
| } | |
| } | |
| return; | |
| } | |
| void CEndlessUsbToolDlg::AddLanguagesToUI() | |
| { | |
| FUNCTION_ENTER; | |
| loc_cmd* lcmd = NULL; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| CComPtr<IHTMLSelectElement> selectElementDualBoot; | |
| HRESULT hr; | |
| hr = GetSelectElement(_T(ELEMENT_LANGUAGE_DUALBOOT), selectElementDualBoot); | |
| IFFALSE_RETURN(SUCCEEDED(hr) && selectElementDualBoot != NULL, "Error returned from GetSelectElement."); | |
| hr = selectElementDualBoot->put_length(0); | |
| int index = 0; | |
| // Add languages to dropdown and apply localization | |
| list_for_each_entry(lcmd, &locale_list, loc_cmd, list) { | |
| luprintf("Language available : %s", lcmd->txt[1]); | |
| hr = AddEntryToSelect(selectElementDualBoot, UTF8ToBSTR(lcmd->txt[0]), UTF8ToBSTR(lcmd->txt[1]), NULL, m_selectedLocale == lcmd ? TRUE : FALSE); | |
| IFFALSE_RETURN(SUCCEEDED(hr), "Error adding the new option element to the select element on the dual boot page"); | |
| } | |
| } | |
| void CEndlessUsbToolDlg::ChangePage(PCTSTR newPage) | |
| { | |
| FUNCTION_ENTER; | |
| static CString currentPage(ELEMENT_DUALBOOT_PAGE); | |
| uprintf("ChangePage requested from %ls to %ls", currentPage, newPage); | |
| if (currentPage == newPage) { | |
| uprintf("ERROR: Already on that page."); | |
| return; | |
| } | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(currentPage), CComVariant(FALSE)); | |
| currentPage = newPage; | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(currentPage), CComVariant(TRUE)); | |
| Analytics::instance()->screenTracking(currentPage); | |
| return; | |
| } | |
| #define MSG_RECOVER_RESUME MSG_326 | |
| #define MSG_RECOVER_DOWNLOAD_AGAIN MSG_327 | |
| #define MSG_RECOVER_TRY_AGAIN MSG_328 | |
| void CEndlessUsbToolDlg::ErrorOccured(ErrorCause_t errorCause) | |
| { | |
| uint32_t recoverButtonMsgId = 0, suggestionMsgId = 0, headlineMsgId = MSG_370; | |
| bool driveLetterInHeading = false; | |
| switch (errorCause) { | |
| case ErrorCause_t::ErrorCauseDownloadFailed: | |
| recoverButtonMsgId = MSG_RECOVER_RESUME; | |
| suggestionMsgId = MSG_323; | |
| break; | |
| case ErrorCause_t::ErrorCauseDownloadFailedDiskFull: | |
| recoverButtonMsgId = MSG_RECOVER_RESUME; | |
| headlineMsgId = MSG_350; | |
| suggestionMsgId = MSG_334; | |
| case ErrorCause_t::ErrorCauseInstallFailedDiskFull: | |
| recoverButtonMsgId = MSG_RECOVER_RESUME; | |
| headlineMsgId = MSG_350; | |
| suggestionMsgId = MSG_351; | |
| break; | |
| case ErrorCause_t::ErrorCauseVerificationFailed: | |
| recoverButtonMsgId = MSG_RECOVER_DOWNLOAD_AGAIN; | |
| suggestionMsgId = MSG_324; | |
| break; | |
| case ErrorCause_t::ErrorCauseCancelled: | |
| case ErrorCause_t::ErrorCauseGeneric: | |
| case ErrorCause_t::ErrorCauseWriteFailed: | |
| case ErrorCause_t::ErrorCauseSuspended: // TODO: new string here | |
| recoverButtonMsgId = MSG_RECOVER_TRY_AGAIN; | |
| suggestionMsgId = m_selectedInstallMethod == InstallMethod_t::InstallDualBoot ? MSG_358 : MSG_325; | |
| break; | |
| case ErrorCause_t::ErrorCauseNot64Bit: | |
| case ErrorCause_t::ErrorCause32BitEFI: | |
| headlineMsgId = MSG_354; | |
| suggestionMsgId = MSG_355; | |
| break; | |
| case ErrorCause_t::ErrorCauseBitLocker: | |
| headlineMsgId = MSG_356; | |
| driveLetterInHeading = true; | |
| suggestionMsgId = MSG_357; | |
| break; | |
| case ErrorCause_t::ErrorCauseNotNTFS: | |
| headlineMsgId = MSG_352; | |
| driveLetterInHeading = true; | |
| suggestionMsgId = MSG_353; | |
| break; | |
| case ErrorCause_t::ErrorCauseNonWindowsMBR: | |
| headlineMsgId = MSG_359; | |
| suggestionMsgId = MSG_360; | |
| break; | |
| default: | |
| uprintf("Unhandled error cause %ls(%d)", ErrorCauseToStr(errorCause), errorCause); | |
| break; | |
| } | |
| // Update the error button text if it's a "recoverable" error case or hide it otherwise | |
| if (recoverButtonMsgId != 0) { | |
| SetElementText(_T(ELEMENT_ERROR_BUTTON), UTF8ToBSTR(lmprintf(recoverButtonMsgId))); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_ERROR_BUTTON)), CComVariant(TRUE)); | |
| } else { | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_ERROR_BUTTON)), CComVariant(FALSE)); | |
| } | |
| if (headlineMsgId != 0) { | |
| CComBSTR headlineMsg; | |
| if (driveLetterInHeading) headlineMsg = UTF8ToBSTR(lmprintf(headlineMsgId, ConvertUnicodeToUTF8(GetSystemDrive()))); | |
| else headlineMsg = UTF8ToBSTR(lmprintf(headlineMsgId)); | |
| SetElementText(_T(ELEMENT_ERROR_MESSAGE), headlineMsg); | |
| } | |
| // Ask user to delete the file that didn't pass signature verification | |
| // Trying again with the same file will result in the same verification error | |
| bool fileVerificationFailed = (errorCause == ErrorCause_t::ErrorCauseVerificationFailed); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_ERROR_DELETE_CHECKBOX), CComVariant(fileVerificationFailed)); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_ERROR_BUTTON))), CComVariant(!fileVerificationFailed)); | |
| CComBSTR deleteFilesText(""); | |
| if (fileVerificationFailed) { | |
| bool isInstallerImage = (m_localFile == m_localInstallerImage.filePath); | |
| ULONGLONG invalidFileSize = isInstallerImage ? m_localInstallerImage.size : m_selectedFileSize; | |
| deleteFilesText = UTF8ToBSTR(lmprintf(MSG_336, SizeToHumanReadable(invalidFileSize, FALSE, use_fake_units))); | |
| CallJavascript(_T(JS_RESET_CHECK), CComVariant(_T(ELEMENT_ERROR_DELETE_CHECKBOX))); | |
| } | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_ERROR_DELETE_CHECKBOX), CComVariant(fileVerificationFailed)); | |
| SetElementText(_T(ELEMENT_ERROR_DELETE_TEXT), deleteFilesText); | |
| // Update the error description and "recovery" suggestion | |
| if (suggestionMsgId != 0) { | |
| CComBSTR message; | |
| if (suggestionMsgId == MSG_334) { | |
| POSITION p = m_remoteImages.FindIndex(m_selectedRemoteIndex); | |
| ULONGLONG size = 0; | |
| if (p != NULL) { | |
| RemoteImageEntry_t remote = m_remoteImages.GetAt(p); | |
| size = remote.compressedSize; | |
| } | |
| // we don't take the signature files into account but we are taking about ~2KB compared to >2GB | |
| ULONGLONG totalSize = size + (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb ? m_installerImage.compressedSize : 0); | |
| message = UTF8ToBSTR(lmprintf(suggestionMsgId, SizeToHumanReadable(totalSize, FALSE, use_fake_units))); | |
| } else if(suggestionMsgId == MSG_351) { | |
| int nrGigsNeeded; | |
| ULONGLONG neededSize = GetNeededSpaceForDualBoot(nrGigsNeeded); | |
| message = UTF8ToBSTR(lmprintf(suggestionMsgId, SizeToHumanReadable(neededSize, FALSE, use_fake_units), ConvertUnicodeToUTF8(GetSystemDrive()))); | |
| } else { | |
| message = UTF8ToBSTR(lmprintf(suggestionMsgId)); | |
| } | |
| SetElementText(_T(ELEMENT_ERROR_SUGGESTION), message); | |
| } | |
| if (m_taskbarProgress != NULL) { | |
| m_taskbarProgress->SetProgressState(m_hWnd, TBPF_ERROR); | |
| m_taskbarProgress->SetProgressValue(m_hWnd, 100, 100); | |
| } | |
| ChangePage(_T(ELEMENT_ERROR_PAGE)); | |
| if (errorCause == ErrorCause_t::ErrorCauseCancelled) | |
| TrackEvent(_T("Cancelled")); | |
| else | |
| TrackEvent(_T("Failed"), ErrorCauseToStr(errorCause)); | |
| bool fatal = FALSE; | |
| switch (errorCause) { | |
| case ErrorCause_t::ErrorCauseDownloadFailed: | |
| case ErrorCause_t::ErrorCauseVerificationFailed: | |
| case ErrorCause_t::ErrorCauseWriteFailed: | |
| fatal = FALSE; | |
| break; | |
| default: | |
| fatal = TRUE; | |
| break; | |
| } | |
| Analytics::instance()->exceptionTracking(ErrorCauseToStr(errorCause), fatal); | |
| } | |
| HRESULT CEndlessUsbToolDlg::GetSelectedOptionElementText(CComPtr<IHTMLSelectElement> selectElem, CString &text) | |
| { | |
| IHTMLOptionElement *optionElem; | |
| HRESULT hr; | |
| IDispatch *dispOpt; | |
| long idx; | |
| _variant_t index; | |
| hr = selectElem->get_selectedIndex(&idx); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error getting selected index"); | |
| index = idx; | |
| hr = selectElem->item(index, index, &dispOpt); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error accessing select element index"); | |
| hr = dispOpt->QueryInterface(IID_IHTMLOptionElement, (void **) &optionElem); | |
| dispOpt->Release(); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error getting option element"); | |
| BSTR bstrText; | |
| hr = optionElem->get_text(&bstrText); | |
| optionElem->Release(); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error getting option text"); | |
| text = CString(bstrText); | |
| SysFreeString(bstrText); | |
| error: | |
| return hr; | |
| } | |
| HRESULT CEndlessUsbToolDlg::UpdateSelectOptionText(CComPtr<IHTMLSelectElement> selectElement, const CString &text, LONG index) | |
| { | |
| CComPtr<IDispatch> pDispatch; | |
| CComPtr<IHTMLOptionElement> pOption; | |
| CComBSTR valueBSTR; | |
| HRESULT hr = selectElement->item(CComVariant(index), CComVariant(index), &pDispatch); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && pDispatch != NULL, "Error when querying for element at requested index."); | |
| hr = pDispatch.QueryInterface<IHTMLOptionElement>(&pOption); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error when querying for IHTMLOptionElement interface"); | |
| hr = pOption->put_text(CComBSTR(text)); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error on IHTMLOptionElement::put_text"); | |
| error: | |
| return hr; | |
| } | |
| HRESULT CEndlessUsbToolDlg::GetSelectElement(PCTSTR selectId, CComPtr<IHTMLSelectElement> &selectElem) | |
| { | |
| CComPtr<IHTMLElement> pElement; | |
| HRESULT hr; | |
| hr = m_spHtmlDoc3->getElementById(CComBSTR(selectId), &pElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && pElement != NULL, "Error when querying for select element."); | |
| hr = pElement.QueryInterface<IHTMLSelectElement>(&selectElem); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error querying for IHTMLSelectElement interface"); | |
| error: | |
| return hr; | |
| } | |
| HRESULT CEndlessUsbToolDlg::ClearSelectElement(PCTSTR selectId) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| HRESULT hr; | |
| hr = GetSelectElement(selectId, selectElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && selectElement != NULL, "Error returned from GetSelectElement"); | |
| hr = selectElement->put_length(0); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error removing all elements from HTML element"); | |
| error: | |
| return hr; | |
| } | |
| HRESULT CEndlessUsbToolDlg::AddEntryToSelect(PCTSTR selectId, const CComBSTR &value, const CComBSTR &text, long *outIndex, BOOL selected) | |
| { | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| HRESULT hr; | |
| hr = GetSelectElement(selectId, selectElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && selectElement != NULL, "Error returned from GetSelectElement"); | |
| return AddEntryToSelect(selectElement, value, text, outIndex, selected); | |
| error: | |
| luprintf("AddEntryToSelect error on select [%ls] and entry (%s, %s)", selectId, text, value); | |
| return hr; | |
| } | |
| HRESULT CEndlessUsbToolDlg::AddEntryToSelect(CComPtr<IHTMLSelectElement> &selectElem, const CComBSTR &value, const CComBSTR &text, long *outIndex, BOOL selected) | |
| { | |
| CComPtr<IHTMLElement> pElement; | |
| CComPtr<IHTMLOptionElement> optionElement; | |
| HRESULT hr; | |
| hr = m_spHtmlDoc->createElement(CComBSTR("option"), &pElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error when creating the option element"); | |
| hr = pElement.QueryInterface<IHTMLOptionElement>(&optionElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error when querying for IHTMLOptionElement interface"); | |
| optionElement->put_selected(selected); | |
| optionElement->put_value(value); | |
| optionElement->put_text(text); | |
| long length; | |
| hr = selectElem->get_length(&length); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error querying the select element length."); | |
| if (outIndex != NULL) { | |
| *outIndex = length; | |
| } | |
| hr = selectElem->add(pElement, CComVariant(length)); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "Error adding the new option element to the select element"); | |
| return S_OK; | |
| error: | |
| luprintf("AddEntryToSelect error on adding entry (%ls, %ls)", text, value); | |
| return hr; | |
| } | |
| bool CEndlessUsbToolDlg::IsButtonDisabled(IHTMLElement *pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLElement> parentElem; | |
| CComBSTR className; | |
| IFFALSE_RETURN_VALUE(pElement != NULL, "IsButtonDisabled pElement is NULL", false); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(pElement->get_parentElement(&parentElem)) && parentElem != NULL, "IsButtonDisabled Error querying for parent element", false); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(parentElem->get_className(&className)) && className != NULL, "IsButtonDisabled Error querying for class name", false); | |
| return wcsstr(className, _T(CLASS_BUTTON_DISABLED)) != NULL; | |
| } | |
| //HRESULT CEndlessUsbToolDlg::OnHtmlSelectStart(IHTMLElement* pElement) | |
| //{ | |
| // POINT pt; | |
| // GetCursorPos(&pt); | |
| // ::SendMessage(m_hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0); | |
| // | |
| // //do not pass the event to the IE server/JavaScript | |
| // return S_FALSE; | |
| //} | |
| void CEndlessUsbToolDlg::TrackEvent(const CString &action, const CString &label, LONGLONG value) | |
| { | |
| Analytics::instance()->eventTracking(InstallMethodToStr(m_selectedInstallMethod), action, label, value); | |
| } | |
| void CEndlessUsbToolDlg::TrackEvent(const CString &action, LONGLONG value) | |
| { | |
| CString label; | |
| label.Format(_T("%I64i"), value); | |
| Analytics::instance()->eventTracking(InstallMethodToStr(m_selectedInstallMethod), action, label, value); | |
| } | |
| void CEndlessUsbToolDlg::SetSelectedInstallMethod(InstallMethod_t method) | |
| { | |
| if (m_selectedInstallMethod == method) return; | |
| m_selectedInstallMethod = method; | |
| TrackEvent(_T("Selected")); | |
| } | |
| #define KEY_PRESSED 0x8000 | |
| // Dual Boot Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnAdvancedOptionsClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| SHORT keyState = GetKeyState(VK_CONTROL); | |
| bool oldStyleUSB = ((keyState & KEY_PRESSED) != 0) || (nWindowsVersion == WINDOWS_XP); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_ADVOPT_SUBTITLE), CComVariant(!oldStyleUSB)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_COMBINED_USB_BUTTON)), CComVariant(!oldStyleUSB)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_USB_LEARN_MORE), CComVariant(!oldStyleUSB)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_REFORMATTER_USB_BUTTON)), CComVariant(oldStyleUSB)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_LIVE_USB_BUTTON)), CComVariant(oldStyleUSB)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_COMPARE_OPTIONS), CComVariant(oldStyleUSB)); | |
| ChangePage(_T(ELEMENT_ADVANCED_PAGE)); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnInstallDualBootClicked(IHTMLElement* pElement) | |
| { | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnInstallDualBootClicked: Button is disabled. ", S_OK); | |
| FUNCTION_ENTER; | |
| CString systemDriveLetter = GetSystemDrive(); | |
| BOOL x64BitSupported = Has64BitSupport() ? TRUE : FALSE; | |
| uprintf("HW processor has 64 bit support: %s", x64BitSupported ? "YES" : "NO"); | |
| CString manufacturer, model; | |
| IFFALSE_PRINTERROR(GetMachineInfo(manufacturer, model), "Couldn't query manufacturer & model"); | |
| uprintf("System manufacturer: %ls; Model: %ls", manufacturer, model); | |
| BOOL isBitLockerEnabled = IsBitlockedDrive(systemDriveLetter.Left(2)); | |
| uprintf("Is BitLocker/device encryption enabled on '%ls': %s", systemDriveLetter, isBitLockerEnabled ? "YES" : "NO"); | |
| bool isBIOS = IsLegacyBIOSBoot(); | |
| ErrorCause cause = ErrorCauseNone; | |
| // TODO: move bitlocker check in here too? | |
| bool canInstall = CanInstallToDrive(systemDriveLetter, isBIOS, cause); | |
| if (ShouldUninstall()) { | |
| QueryAndDoUninstall(); | |
| return S_OK; | |
| } | |
| SetSelectedInstallMethod(InstallMethod_t::InstallDualBoot); | |
| TrackEvent(L"FirmwareType", isBIOS ? L"BIOS" : L"EFI"); | |
| TrackEvent(L"Manufacturer", manufacturer); | |
| TrackEvent(L"Model", model); | |
| if (!x64BitSupported) { | |
| ErrorOccured(ErrorCauseNot64Bit); | |
| } else if (isBitLockerEnabled) { | |
| ErrorOccured(ErrorCauseBitLocker); | |
| } else if (!canInstall) { | |
| ErrorOccured(cause); | |
| } else { | |
| GoToSelectFilePage(); | |
| } | |
| return S_OK; | |
| } | |
| // Advanced Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnLiveUsbClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| SetSelectedInstallMethod(InstallMethod_t::LiveUsb); | |
| GoToSelectFilePage(); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnReformatterUsbClicked(IHTMLElement* pElement) | |
| { | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnReformatterUsbClicked: Button is disabled. ", S_OK); | |
| FUNCTION_ENTER; | |
| SetSelectedInstallMethod(InstallMethod_t::ReformatterUsb); | |
| GoToSelectFilePage(); | |
| return S_OK; | |
| } | |
| void CEndlessUsbToolDlg::GoToSelectFilePage() | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLElement> selectElem; | |
| CComBSTR sizeText; | |
| RemoteImageEntry_t r; | |
| // Check locally available images | |
| UpdateFileEntries(true); | |
| // Update UI based on what was found | |
| bool canUseLocal = CanUseLocalFile(); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_LOCAL_FILES_FOUND), CComVariant(canUseLocal)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_LOCAL_FILES_NOT_FOUND), CComVariant(!canUseLocal)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(HTML_BUTTON_ID(ELEMENT_SELFILE_NEXT_BUTTON)), CComVariant(canUseLocal)); | |
| if (canUseLocal) { | |
| SetElementText(_T(ELEMENT_SET_FILE_TITLE), UTF8ToBSTR(lmprintf(MSG_321))); | |
| SetElementText(_T(ELEMENT_SET_FILE_SUBTITLE), UTF8ToBSTR(lmprintf(MSG_322))); | |
| } else if(CanUseRemoteFile()) { | |
| ApplyRufusLocalization(); | |
| } else { | |
| uprintf("No remote images available and no local images available."); | |
| } | |
| // Update page with no local files found | |
| POSITION p = m_remoteImages.FindIndex(m_baseImageRemoteIndex); | |
| if (p != NULL) { | |
| r = m_remoteImages.GetAt(p); | |
| // Update light download size | |
| SetElementText(_T(ELEMENT_DOWNLOAD_LIGHT_SIZE), GetDownloadString(r)); | |
| // Update full download size | |
| HRESULT hr = GetElement(_T(ELEMENT_REMOTE_SELECT), &selectElem); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr), "GoToSelectFilePage: querying for local select element."); | |
| bool useLocalFile = m_useLocalFile; | |
| OnSelectedRemoteFileChanged(selectElem); | |
| m_useLocalFile = useLocalFile; | |
| } | |
| ChangePage(_T(ELEMENT_FILE_PAGE)); | |
| return; | |
| error: | |
| ErrorOccured(ErrorCause_t::ErrorCauseGeneric); | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnLinkClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComBSTR id; | |
| HRESULT hr; | |
| uint32_t msg_id = 0; | |
| char *url = NULL; | |
| IFFALSE_RETURN_VALUE(pElement != NULL, "OnLinkClicked: Error getting element id", S_OK); | |
| hr = pElement->get_id(&id); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "OnLinkClicked: Error getting element id", S_OK); | |
| if (id == _T(ELEMENT_COMPARE_OPTIONS)) { | |
| msg_id = MSG_312; | |
| } else if (id == _T(ELEMENT_ENDLESS_SUPPORT) || id == _T(ELEMENT_CONNECTED_SUPPORT_LINK) || id == _T(ELEMENT_STORAGE_SUPPORT_LINK)) { | |
| msg_id = MSG_314; | |
| } else if (id == _T(ELEMENT_CONNECTED_LINK)) { | |
| WinExec("c:\\windows\\system32\\control.exe ncpa.cpl", SW_NORMAL); | |
| } else if (id == _T(ELEMENT_USBBOOT_HOWTO)) { | |
| msg_id = MSG_329; | |
| } else if (id == _T(ELEMENT_USB_LEARN_MORE)) { | |
| msg_id = MSG_371; | |
| } else if (id == _T(ELEMENT_VERSION_LINK)) { | |
| url = RELEASE_VER_TAG_URL; | |
| } else { | |
| uprintf("Unknown link clicked %ls", id); | |
| } | |
| if (msg_id != 0) { | |
| url = lmprintf(msg_id); | |
| } | |
| if (url != NULL) { | |
| // Radu: do we care about the errors? | |
| ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); | |
| } | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnLanguageChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| CComBSTR selectedValue; | |
| HRESULT hr = pElement->QueryInterface(IID_IHTMLSelectElement, (void**)&selectElement); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && selectElement != NULL, "Error querying for IHTMLSelectElement interface"); | |
| hr = selectElement->get_value(&selectedValue); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hr) && selectElement != NULL, "Error getting selected language value"); | |
| char* p = _com_util::ConvertBSTRToString(selectedValue); | |
| Analytics::instance()->setLanguage(CString(p)); | |
| m_selectedLocale = get_locale_from_name(p, TRUE); | |
| delete[] p; | |
| selected_langid = get_language_id(m_selectedLocale); | |
| reinit_localization(); | |
| msg_table = NULL; | |
| if (get_loc_data_file(m_localizationFile, m_selectedLocale)) | |
| uprintf("Save locale to settings?"); | |
| ApplyRufusLocalization(); | |
| m_selectedRemoteIndex = -1; | |
| AddDownloadOptionsToUI(); | |
| UpdateDualBootTexts(); | |
| return S_OK; | |
| error: | |
| //RADU: what to do on error? | |
| return S_OK; | |
| } | |
| void CEndlessUsbToolDlg::UpdateFileEntries(bool shouldInit) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLElement> pElement; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| HRESULT hr; | |
| WIN32_FIND_DATA findFileData; | |
| POSITION position; | |
| pFileImageEntry_t currentEntry = NULL; | |
| CString currentPath; | |
| BOOL fileAccessException = false; | |
| CString currentInstallerVersion; | |
| CString searchPath = GET_IMAGE_PATH(ALL_FILES); | |
| HANDLE findFilesHandle = FindFirstFile(searchPath, &findFileData); | |
| m_localFilesScanned = true; | |
| // get needed HTML elements | |
| hr = GetSelectElement(_T(ELEMENT_FILES_SELECT), selectElement); | |
| IFFALSE_RETURN(SUCCEEDED(hr), "Error returned from GetSelectElement"); | |
| if (m_imageFiles.IsEmpty()) { | |
| hr = selectElement->put_length(0); | |
| IFFALSE_RETURN(SUCCEEDED(hr), "Error removing all elements from HTML element"); | |
| } | |
| // mark all files as not present | |
| for (position = m_imageFiles.GetStartPosition(); position != NULL; ) { | |
| m_imageFiles.GetNextAssoc(position, currentPath, currentEntry); | |
| currentEntry->stillPresent = FALSE; | |
| } | |
| m_localInstallerImage.stillPresent = FALSE; | |
| if (findFilesHandle == INVALID_HANDLE_VALUE) { | |
| uprintf("UpdateFileEntries: No files found in current directory [%ls]", CEndlessUsbToolApp::m_appDir); | |
| goto checkEntries; | |
| } | |
| do { | |
| if ((findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) continue; | |
| CString currentFile = findFileData.cFileName; | |
| bool isUnpackedFile = false; | |
| // support being run from a USB drive | |
| CString liveFilePath = GET_IMAGE_PATH(EXFAT_ENDLESS_LIVE_FILE_NAME); | |
| if (currentFile == ENDLESS_IMG_FILE_NAME && PathFileExists(liveFilePath)) { | |
| uprintf("Found %ls; checking %ls to find image name", currentFile, liveFilePath); | |
| FILE *liveFile = NULL; | |
| char originalFileName[MAX_PATH]; | |
| memset(originalFileName, 0, MAX_PATH); | |
| errno_t result = _wfopen_s(&liveFile, liveFilePath, L"r"); | |
| if (result == 0) { | |
| fread(originalFileName, 1, MAX_PATH - 1, liveFile); | |
| if (feof(liveFile)) { | |
| currentFile = UTF8ToCString(originalFileName); | |
| currentFile.TrimRight(); | |
| isUnpackedFile = true; | |
| uprintf("image name is %ls", currentFile); | |
| } | |
| fclose(liveFile); | |
| } | |
| if (!isUnpackedFile) { | |
| uprintf("couldn't determine the unpacked image's true name"); | |
| } | |
| } | |
| CString fullPathFile = GET_IMAGE_PATH(currentFile); | |
| CString unpackedFullPathFile = GET_IMAGE_PATH(ENDLESS_IMG_FILE_NAME); | |
| CString extension = CSTRING_GET_LAST(currentFile, '.'); | |
| if (!isUnpackedFile && extension != _T(FILE_GZ_EXTENSION) && extension != _T(FILE_XZ_EXTENSION)) continue; | |
| if (!isUnpackedFile && !PathFileExists(fullPathFile)) continue; // file is present | |
| if (!PathFileExists(fullPathFile + SIGNATURE_FILE_EXT)) continue; // signature file is present | |
| try { | |
| CString displayName, personality, version, date; | |
| bool isInstallerImage = false; | |
| CString fileNameToParse = isUnpackedFile ? (currentFile + L"." + _T(FILE_GZ_EXTENSION)) : currentFile; | |
| if (!ParseImgFileName(fileNameToParse, personality, version, date, isInstallerImage)) continue; | |
| if (!isUnpackedFile && 0 == GetExtractedSize(fullPathFile, isInstallerImage)) continue; | |
| CFile file(isUnpackedFile ? unpackedFullPathFile : fullPathFile, CFile::modeRead); | |
| GetImgDisplayName(displayName, version, personality, file.GetLength()); | |
| if (isInstallerImage) { | |
| if (version > currentInstallerVersion) { | |
| currentInstallerVersion = version; | |
| m_localInstallerImage.stillPresent = TRUE; | |
| m_localInstallerImage.filePath = file.GetFilePath(); | |
| m_localInstallerImage.size = file.GetLength(); | |
| } | |
| } else { | |
| if (IsDualBootOrCombinedUsb() && !HasImageBootSupport(version, date)) { | |
| uprintf("Skiping '%ls' because it doesn't have image boot support.", file.GetFileName()); | |
| continue; | |
| } | |
| // add entry to list or update it | |
| pFileImageEntry_t currentEntry = NULL; | |
| if (!m_imageFiles.Lookup(file.GetFilePath(), currentEntry)) { | |
| currentEntry = new FileImageEntry_t; | |
| currentEntry->autoAdded = TRUE; | |
| currentEntry->filePath = file.GetFilePath(); | |
| currentEntry->size = file.GetLength(); | |
| currentEntry->personality = personality; | |
| currentEntry->version = version; | |
| currentEntry->date = date; | |
| currentEntry->isUnpackedImage = isUnpackedFile; | |
| AddEntryToSelect(selectElement, CComBSTR(currentEntry->filePath), CComBSTR(displayName), ¤tEntry->htmlIndex, 0); | |
| IFFALSE_RETURN(SUCCEEDED(hr), "Error adding item in image file list."); | |
| m_imageFiles.SetAt(currentEntry->filePath, currentEntry); | |
| m_imageIndexToPath.AddTail(currentEntry->filePath); | |
| } else { | |
| UpdateSelectOptionText(selectElement, displayName, currentEntry->htmlIndex); | |
| } | |
| currentEntry->stillPresent = TRUE; | |
| CString basePath = isUnpackedFile ? CSTRING_GET_PATH(fullPathFile, '.') : CSTRING_GET_PATH(CSTRING_GET_PATH(fullPathFile, '.'), '.'); | |
| currentEntry->bootArchivePath = basePath + BOOT_ARCHIVE_SUFFIX; | |
| currentEntry->bootArchiveSigPath = basePath + BOOT_ARCHIVE_SUFFIX + SIGNATURE_FILE_EXT; | |
| currentEntry->unpackedImgSigPath = basePath + IMAGE_FILE_EXT + SIGNATURE_FILE_EXT; | |
| currentEntry->hasBootArchive = PathFileExists(currentEntry->bootArchivePath); | |
| currentEntry->hasBootArchiveSig = PathFileExists(currentEntry->bootArchiveSigPath); | |
| currentEntry->hasUnpackedImgSig = PathFileExists(currentEntry->unpackedImgSigPath); | |
| } | |
| uprintf("Found local image '%ls'", file.GetFilePath()); | |
| // RADU: do we need to care about the size? | |
| } catch (CFileException *ex) { | |
| uprintf("CFileException on file [%ls] with cause [%d] and OS error [%d]", findFileData.cFileName, ex->m_cause, ex->m_lOsError); | |
| ex->Delete(); | |
| fileAccessException = TRUE; | |
| } | |
| } while (FindNextFile(findFilesHandle, &findFileData) != 0); | |
| checkEntries: | |
| int htmlIndexChange = 0; | |
| for (position = m_imageIndexToPath.GetHeadPosition(); position != NULL; ) { | |
| POSITION currentPosition = position; | |
| currentPath = m_imageIndexToPath.GetNext(position); | |
| if (!m_imageFiles.Lookup(currentPath, currentEntry)) { | |
| uprintf("Error: path [%s] not found anymore, removing."); | |
| m_imageIndexToPath.RemoveAt(currentPosition); | |
| continue; | |
| } | |
| if (!currentEntry->autoAdded) { | |
| findFilesHandle = FindFirstFile(currentEntry->filePath, &findFileData); | |
| if (findFilesHandle != INVALID_HANDLE_VALUE) { | |
| currentEntry->stillPresent = TRUE; | |
| } | |
| } | |
| if (htmlIndexChange != 0) { | |
| uprintf("Changing HTML index with %d for %ls", htmlIndexChange, currentEntry->filePath); | |
| currentEntry->htmlIndex -= htmlIndexChange; | |
| } | |
| if (!currentEntry->stillPresent) { | |
| uprintf("Removing %ls", currentPath); | |
| selectElement->remove(currentEntry->htmlIndex); | |
| delete currentEntry; | |
| m_imageFiles.RemoveKey(currentPath); | |
| htmlIndexChange++; | |
| m_imageIndexToPath.RemoveAt(currentPosition); | |
| } | |
| } | |
| FindClose(findFilesHandle); | |
| // if there was an exception when accessing one of the files do a retry in 1 second | |
| if (fileAccessException) { | |
| SetTimer(TID_UPDATE_FILES, 1000, RefreshTimer); | |
| } else { | |
| KillTimer(TID_UPDATE_FILES); | |
| } | |
| bool hasLocalImages = m_imageFiles.GetCount() != 0; | |
| if (!hasLocalImages) { | |
| m_useLocalFile = false; | |
| } | |
| // set selected image for Rufus code | |
| CComBSTR selectedValue; | |
| hr = selectElement->get_value(&selectedValue); | |
| IFFALSE_RETURN(SUCCEEDED(hr) && selectElement != NULL, "Error getting selected file value"); | |
| if (m_useLocalFile) { | |
| safe_free(image_path); | |
| if (selectedValue != NULL) { | |
| image_path = wchar_to_utf8(selectedValue); | |
| } | |
| } | |
| if (shouldInit) { | |
| //// start the change notifications thread | |
| //if (hasLocalImages && m_fileScanThread == INVALID_HANDLE_VALUE) { | |
| // m_fileScanThread = CreateThread(NULL, 0, CEndlessUsbToolDlg::FileScanThread, (LPVOID)this, 0, NULL); | |
| //} | |
| } | |
| } | |
| DWORD WINAPI CEndlessUsbToolDlg::FileScanThread(void* param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| DWORD error = 0; | |
| HANDLE handlesToWaitFor[2]; | |
| DWORD changeNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME; | |
| //changeNotifyFilter |= FILE_NOTIFY_CHANGE_SIZE; | |
| handlesToWaitFor[0] = dlg->m_closeFileScanThreadEvent; | |
| handlesToWaitFor[1] = FindFirstChangeNotification(CEndlessUsbToolApp::m_appDir, FALSE, changeNotifyFilter); | |
| if (handlesToWaitFor[1] == INVALID_HANDLE_VALUE) { | |
| error = GetLastError(); | |
| uprintf("Error on FindFirstChangeNotificationA error=[%d]", error); | |
| return error; | |
| } | |
| while (TRUE) | |
| { | |
| DWORD dwWaitStatus = WaitForMultipleObjects(2, handlesToWaitFor, FALSE, INFINITE); | |
| switch (dwWaitStatus) | |
| { | |
| case WAIT_OBJECT_0: | |
| uprintf("FileScanThread: exit requested."); | |
| FindCloseChangeNotification(handlesToWaitFor[1]); | |
| return 0; | |
| case WAIT_OBJECT_0 + 1: | |
| ::PostMessage(hMainDialog, WM_FILES_CHANGED, 0, 0); | |
| if (FindNextChangeNotification(handlesToWaitFor[1]) == FALSE) { | |
| error = GetLastError(); | |
| uprintf("FileScanThread ERROR: FindNextChangeNotification function failed with error [%d].", GetLastError()); | |
| return error; | |
| } | |
| break; | |
| default: | |
| uprintf("FileScanThread: unhandled wait status [%d]", dwWaitStatus); | |
| return GetLastError(); | |
| } | |
| } | |
| return 0; | |
| } | |
| void CEndlessUsbToolDlg::StartJSONDownload() | |
| { | |
| FUNCTION_ENTER; | |
| char tmp_path[MAX_PATH] = ""; | |
| bool success; | |
| if (m_remoteImages.GetCount() != 0) { | |
| uprintf("List of remote images already downloaded"); | |
| return; | |
| } | |
| // Add both JSONs to download, maybe user goes back and switches between Try and Install | |
| #ifdef ENABLE_JSON_COMPRESSION | |
| CString liveJson(JSON_PACKED(JSON_LIVE_FILE)); | |
| CString installerJson(JSON_PACKED(JSON_INSTALLER_FILE)); | |
| #else | |
| CString liveJson(JSON_LIVE_FILE); | |
| CString installerJson(JSON_INSTALLER_FILE); | |
| #endif // ENABLE_JSON_COMPRESSION | |
| liveJson = CEndlessUsbToolApp::TempFilePath(liveJson); | |
| installerJson = CEndlessUsbToolApp::TempFilePath(installerJson); | |
| ListOfStrings urls = { JSON_URL(JSON_LIVE_FILE), JSON_URL(JSON_INSTALLER_FILE) }; | |
| ListOfStrings files = { liveJson, installerJson }; | |
| success = m_downloadManager.AddDownload(DownloadType_t::DownloadTypeReleseJson, urls, files, true); | |
| if (!success) { | |
| // If resuming a possibly-existing download failed, try harder to start a new one. | |
| // In theory this shouldn't be necessary but this is done for the image download case so who knows! | |
| success = m_downloadManager.AddDownload(DownloadType_t::DownloadTypeReleseJson, urls, files, false); | |
| } | |
| if (!success) { | |
| uprintf("AddDownload failed"); | |
| SetJSONDownloadState(JSONDownloadState::Failed); | |
| } | |
| } | |
| #define JSON_IMAGES "images" | |
| #define JSON_VERSION "version" | |
| #define JSON_IMG_PERSONALITIES "personalities" | |
| #define JSON_IMG_ARCH "arch" | |
| #define JSON_IMG_BRANCH "branch" | |
| #define JSON_IMG_PERS_IMAGES "personality_images" | |
| #define JSON_IMG_PRODUCT "product" | |
| #define JSON_IMG_PLATFORM "platform" | |
| #define JSON_IMG_VERSION "version" | |
| #define JSON_IMG_FULL "full" | |
| #define JSON_IMG_BOOT "boot" | |
| #define JSON_IMG_COMPRESSED_SIZE "compressed_size" | |
| #define JSON_IMG_EXTRACTED_SIZE "extracted_size" | |
| #define JSON_IMG_URL_FILE "file" | |
| #define JSON_IMG_URL_SIG "signature" | |
| #define JSON_IMG_UNPACKED_URL_SIG "extracted_signature" | |
| #define CHECK_ENTRY(parentValue, tag) \ | |
| do { \ | |
| IFFALSE_CONTINUE(!parentValue[tag].isNull(), CString("\t\t Elem is NULL - ") + tag); \ | |
| uprintf("\t\t %s=%s", tag, parentValue[tag].toStyledString().c_str()); \ | |
| } while(false); | |
| bool CEndlessUsbToolDlg::UnpackFile(const CString &archive, const CString &destination, int compressionType, void* progress_function, unsigned long* cancel_request) | |
| { | |
| FUNCTION_ENTER; | |
| int64_t result = -1; | |
| if (compressionType == 0) { | |
| compressionType = GetCompressionType(archive); | |
| IFFALSE_GOTOERROR(compressionType != BLED_COMPRESSION_NONE, "can't determine compression type"); | |
| } | |
| // RADU: provide a progress function and move this from UI thread | |
| // For initial release this is ok as the operation should be very fast for the JSON | |
| // Unpack the file | |
| result = bled_init(_uprintf, (progress_t)progress_function, cancel_request); | |
| result = bled_uncompress(ConvertUnicodeToUTF8(archive), ConvertUnicodeToUTF8(destination), compressionType); | |
| bled_exit(); | |
| error: | |
| return result >= 0; | |
| } | |
| static bool FindLatestVersion(const Json::Value &rootValue, Json::Value &latestEntry) | |
| { | |
| FUNCTION_ENTER; | |
| Json::Value jsonElem, imagesElem, personalities, persImages; | |
| ImageVersion latestParsedVersion; | |
| // Print version | |
| jsonElem = rootValue[JSON_VERSION]; | |
| if (!jsonElem.isString()) uprintf("JSON Version: %s", jsonElem.asString().c_str()); | |
| // Go through the image entries | |
| imagesElem = rootValue[JSON_IMAGES]; | |
| IFFALSE_GOTOERROR(!jsonElem.isNull(), "Missing element " JSON_IMAGES); | |
| for (Json::ValueIterator it = imagesElem.begin(); it != imagesElem.end(); it++) { | |
| uprintf("Found entry '%s'", it.memberName()); | |
| CHECK_ENTRY((*it), JSON_IMG_ARCH); | |
| CHECK_ENTRY((*it), JSON_IMG_BRANCH); | |
| CHECK_ENTRY((*it), JSON_IMG_PRODUCT); | |
| CHECK_ENTRY((*it), JSON_IMG_PLATFORM); | |
| CHECK_ENTRY((*it), JSON_IMG_VERSION); | |
| CHECK_ENTRY((*it), JSON_IMG_PERSONALITIES); | |
| personalities = (*it)[JSON_IMG_PERSONALITIES]; | |
| IFFALSE_CONTINUE(personalities.isArray(), "Error: No valid personality_images entry found."); | |
| persImages = (*it)[JSON_IMG_PERS_IMAGES]; | |
| IFFALSE_CONTINUE(!persImages.isNull(), "Error: No personality_images entry found."); | |
| const char *versionString = (*it)[JSON_IMG_VERSION].asCString(); | |
| ImageVersion currentParsedVersion; | |
| if (!ImageVersion::Parse(versionString, currentParsedVersion)) { | |
| uprintf("Can't parse version '%s'", versionString); | |
| continue; | |
| } | |
| if (currentParsedVersion > latestParsedVersion) { | |
| latestParsedVersion = currentParsedVersion; | |
| latestEntry = *it; | |
| } | |
| } | |
| IFFALSE_GOTOERROR(!latestEntry.isNull(), "No images found in the JSON."); | |
| return true; | |
| error: | |
| return false; | |
| } | |
| bool CEndlessUsbToolDlg::ParseJsonFile(LPCTSTR filename, bool isInstallerJson) | |
| { | |
| FUNCTION_ENTER; | |
| Json::Reader reader; | |
| Json::Value rootValue, personalities, persImages, persImage, fullImage, latestEntry, bootImage; | |
| CString latestVersion(""); | |
| std::ifstream jsonStream; | |
| jsonStream.open(filename); | |
| IFFALSE_GOTOERROR(!jsonStream.fail(), "Opening JSON file failed."); | |
| IFFALSE_GOTOERROR(reader.parse(jsonStream, rootValue, false), "Parsing of JSON failed."); | |
| IFFALSE_GOTOERROR(FindLatestVersion(rootValue, latestEntry), "No images found in the JSON."); | |
| if (!latestEntry.isNull()) { | |
| latestVersion = latestEntry[JSON_IMG_VERSION].asCString(); | |
| uprintf("Selected version '%ls'", latestVersion); | |
| m_downloadManager.SetLatestEosVersion(latestVersion); | |
| uint32_t personalityMsgId = 0; | |
| personalities = latestEntry[JSON_IMG_PERSONALITIES]; | |
| persImages = latestEntry[JSON_IMG_PERS_IMAGES]; | |
| for (Json::ValueIterator persIt = personalities.begin(); persIt != personalities.end(); persIt++) { | |
| IFFALSE_CONTINUE(persIt->isString(), "Entry is not string, continuing"); | |
| IFFALSE_CONTINUE(!isInstallerJson || CString(persIt->asCString()) == PERSONALITY_BASE, "Installer JSON parsing: not base personality"); | |
| IFFALSE_CONTINUE(m_personalityToLocaleMsg.Lookup(CString(persIt->asCString()), personalityMsgId), "Unknown personality. Continuing."); | |
| persImage = persImages[persIt->asString()]; | |
| IFFALSE_CONTINUE(!persImage.isNull(), CString("Personality image entry not found - ") + persIt->asCString()); | |
| fullImage = persImage[JSON_IMG_FULL]; | |
| IFFALSE_CONTINUE(!fullImage.isNull(), CString("'full' entry not found for personality - ") + persIt->asCString()); | |
| CHECK_ENTRY(fullImage, JSON_IMG_COMPRESSED_SIZE); | |
| CHECK_ENTRY(fullImage, JSON_IMG_EXTRACTED_SIZE); | |
| CHECK_ENTRY(fullImage, JSON_IMG_URL_FILE); | |
| CHECK_ENTRY(fullImage, JSON_IMG_URL_SIG); | |
| CHECK_ENTRY(fullImage, JSON_IMG_UNPACKED_URL_SIG); | |
| RemoteImageEntry_t remoteImageEntry; | |
| RemoteImageEntry_t &remoteImage = isInstallerJson ? m_installerImage : remoteImageEntry; | |
| remoteImage.compressedSize = fullImage[JSON_IMG_COMPRESSED_SIZE].asUInt64(); | |
| remoteImage.extractedSize = fullImage[JSON_IMG_EXTRACTED_SIZE].asUInt64(); | |
| // before releases extractedSize is not populated; add the compressed size so we don't show 0 | |
| if(remoteImage.extractedSize == 0) remoteImage.extractedSize = remoteImage.compressedSize; | |
| remoteImage.urlFile = fullImage[JSON_IMG_URL_FILE].asCString(); | |
| remoteImage.urlSignature = fullImage[JSON_IMG_URL_SIG].asCString(); | |
| remoteImage.personality = persIt->asCString(); | |
| remoteImage.version = latestVersion; | |
| remoteImage.urlUnpackedSignature = fullImage[JSON_IMG_UNPACKED_URL_SIG].asCString(); | |
| if(!isInstallerJson) { | |
| bootImage = persImage[JSON_IMG_BOOT]; | |
| IFFALSE_CONTINUE(!bootImage.isNull(), CString("'boot' entry not found for personality - ") + persIt->asCString()); | |
| CHECK_ENTRY(bootImage, JSON_IMG_COMPRESSED_SIZE); | |
| CHECK_ENTRY(bootImage, JSON_IMG_URL_FILE); | |
| CHECK_ENTRY(bootImage, JSON_IMG_URL_SIG); | |
| remoteImage.urlBootArchive = bootImage[JSON_IMG_URL_FILE].asCString(); | |
| remoteImage.urlBootArchiveSignature = bootImage[JSON_IMG_URL_SIG].asCString(); | |
| remoteImage.bootArchiveSize = bootImage[JSON_IMG_COMPRESSED_SIZE].asUInt64(); | |
| // Create display name | |
| GetImgDisplayName(remoteImage.displayName, remoteImage.version, remoteImage.personality, remoteImage.compressedSize); | |
| // Create dowloadJobName | |
| remoteImage.downloadJobName = latestVersion; | |
| remoteImage.downloadJobName += persIt->asCString(); | |
| m_remoteImages.AddTail(remoteImage); | |
| } | |
| } | |
| } | |
| return true; | |
| error: | |
| uprintf("JSON parsing failed. Parser error messages %s", reader.getFormattedErrorMessages().c_str()); | |
| return false; | |
| } | |
| void CEndlessUsbToolDlg::UpdateDownloadOptions() | |
| { | |
| FUNCTION_ENTER; | |
| CString filePath; | |
| #ifdef ENABLE_JSON_COMPRESSION | |
| CString filePathGz; | |
| #endif // ENABLE_JSON_COMPRESSION | |
| m_remoteImages.RemoveAll(); | |
| // Parse JSON with normal images | |
| filePath = CEndlessUsbToolApp::TempFilePath(CString(JSON_LIVE_FILE)); | |
| #ifdef ENABLE_JSON_COMPRESSION | |
| filePathGz = CEndlessUsbToolApp::TempFilePath(CString(JSON_PACKED(JSON_LIVE_FILE))); | |
| IFFALSE_GOTOERROR(UnpackFile(filePathGz, filePath, BLED_COMPRESSION_GZIP), "Error uncompressing eos JSON file."); | |
| #endif // ENABLE_JSON_COMPRESSION | |
| IFFALSE_GOTOERROR(ParseJsonFile(filePath, false), "Error parsing eos JSON file."); | |
| // Parse JSON with installer images | |
| filePath = CEndlessUsbToolApp::TempFilePath(CString(JSON_INSTALLER_FILE)); | |
| #ifdef ENABLE_JSON_COMPRESSION | |
| filePathGz = CEndlessUsbToolApp::TempFilePath(CString(JSON_PACKED(JSON_INSTALLER_FILE))); | |
| IFFALSE_GOTOERROR(UnpackFile(filePathGz, filePath, BLED_COMPRESSION_GZIP), "Error uncompressing eosinstaller JSON file."); | |
| #endif // ENABLE_JSON_COMPRESSION | |
| IFFALSE_GOTOERROR(ParseJsonFile(filePath, true), "Error parsing eosinstaller JSON file."); | |
| AddDownloadOptionsToUI(); | |
| SetJSONDownloadState(JSONDownloadState::Succeeded); | |
| return; | |
| error: | |
| // TODO: on error, retry download? | |
| SetJSONDownloadState(JSONDownloadState::Failed); | |
| } | |
| void CEndlessUsbToolDlg::AddDownloadOptionsToUI() | |
| { | |
| CString languagePersonalty = PERSONALITY_ENGLISH; | |
| // remove all options from UI | |
| HRESULT hr = ClearSelectElement(_T(ELEMENT_REMOTE_SELECT)); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error clearing remote images select."); | |
| hr = ClearSelectElement(_T(ELEMENT_SELFILE_DOWN_LANG)); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error clearing remote images select."); | |
| // get selected language | |
| if (!m_localeToPersonality.Lookup(m_selectedLocale->txt[0], languagePersonalty)) { | |
| uprintf("ERROR: Selected language personality not found. Defaulting to English"); | |
| } | |
| // add options to UI | |
| long selectIndex = -1; | |
| for (POSITION pos = m_remoteImages.GetHeadPosition(); pos != NULL; ) { | |
| RemoteImageEntry_t imageEntry = m_remoteImages.GetNext(pos); | |
| bool matchesLanguage = languagePersonalty == imageEntry.personality; | |
| hr = AddEntryToSelect(_T(ELEMENT_REMOTE_SELECT), CComBSTR(""), CComBSTR(imageEntry.displayName), &selectIndex, matchesLanguage); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error adding remote image to list."); | |
| // option size | |
| CComBSTR sizeText = GetDownloadString(imageEntry); | |
| const wchar_t *htmlElemId = NULL; | |
| if (imageEntry.personality == PERSONALITY_BASE) { | |
| m_baseImageRemoteIndex = selectIndex; | |
| htmlElemId = _T(ELEMENT_DOWNLOAD_LIGHT_SIZE); | |
| } | |
| else { | |
| CString imageLanguage = UTF8ToCString(lmprintf(m_personalityToLocaleMsg[imageEntry.personality])); | |
| CString indexStr; | |
| indexStr.Format(L"%d", selectIndex); | |
| hr = AddEntryToSelect(_T(ELEMENT_SELFILE_DOWN_LANG), CComBSTR(indexStr), CComBSTR(imageLanguage), NULL, matchesLanguage); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error adding remote image to full images list."); | |
| if (matchesLanguage) { | |
| htmlElemId = _T(ELEMENT_DOWNLOAD_FULL_SIZE); | |
| } | |
| } | |
| if (htmlElemId != 0) { | |
| SetElementText(htmlElemId, sizeText); | |
| } | |
| } | |
| if (m_imageFiles.GetCount() == 0) { | |
| m_selectedRemoteIndex = m_baseImageRemoteIndex; | |
| } | |
| } | |
| void CEndlessUsbToolDlg::UpdateDownloadableState() | |
| { | |
| FUNCTION_ENTER; | |
| HRESULT hr; | |
| bool foundRemoteImages = m_remoteImages.GetCount() != 0; | |
| // Show JSON download errors as connection errors. Maybe a proxy is mangling the JSON? | |
| bool connected = m_isConnected | |
| && m_jsonDownloadState != JSONDownloadState::Failed | |
| && m_jsonDownloadState != JSONDownloadState::Retrying; | |
| hr = CallJavascript(_T(JS_ENABLE_DOWNLOAD), CComVariant(foundRemoteImages), CComVariant(connected)); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error calling " JS_ENABLE_DOWNLOAD "()"); | |
| bool foundBaseImage = m_baseImageRemoteIndex != -1; | |
| hr = CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_DOWNLOAD_LIGHT_BUTTON))), CComVariant(m_isConnected && foundBaseImage)); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hr), "Error enabling 'Light' button"); | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnAdvancedPagePreviousClicked(IHTMLElement* pElement) | |
| { | |
| ChangePage(_T(ELEMENT_DUALBOOT_PAGE)); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnCombinedUsbButtonClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| SetSelectedInstallMethod(InstallMethod_t::CombinedUsb); | |
| GoToSelectFilePage(); | |
| return S_OK; | |
| } | |
| // Select File Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnSelectFilePreviousClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| ChangePage(m_selectedInstallMethod == InstallMethod_t::InstallDualBoot ? _T(ELEMENT_DUALBOOT_PAGE) : _T(ELEMENT_ADVANCED_PAGE)); | |
| SetSelectedInstallMethod(None); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectFileNextClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| LPITEMIDLIST pidlDesktop = NULL; | |
| SHChangeNotifyEntry NotifyEntry; | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnSelectFileNextClicked: Button is disabled. ", S_OK); | |
| // Get display name with actual image size, not compressed | |
| CString selectedImage, personality, version; | |
| ULONGLONG size = 0; | |
| bool willUnpackImage = m_selectedInstallMethod == LiveUsb || m_selectedInstallMethod == CombinedUsb; | |
| if (m_useLocalFile) { | |
| selectedImage = UTF8ToCString(image_path); | |
| pFileImageEntry_t localEntry = NULL; | |
| m_imageFiles.Lookup(selectedImage, localEntry); | |
| if (localEntry == NULL) { | |
| uprintf("ERROR: Selected local file not found."); | |
| ErrorOccured(ErrorCause_t::ErrorCauseGeneric); | |
| return S_OK; | |
| } | |
| personality = localEntry->personality; | |
| version = localEntry->version; | |
| size = (willUnpackImage && !localEntry->isUnpackedImage) ? GetExtractedSize(selectedImage, FALSE) : localEntry->size; | |
| m_selectedFileSize = localEntry->size; | |
| } else { | |
| POSITION p = m_remoteImages.FindIndex(m_selectedRemoteIndex); | |
| if (p == NULL) { | |
| uprintf("Remote index value not valid."); | |
| ErrorOccured(ErrorCause_t::ErrorCauseGeneric); | |
| return S_OK; | |
| } | |
| RemoteImageEntry_t remote = m_remoteImages.GetAt(p); | |
| personality = remote.personality; | |
| version = remote.version; | |
| selectedImage = CSTRING_GET_LAST(remote.urlFile, '/'); | |
| CString selectedImagePath = GET_IMAGE_PATH(selectedImage); | |
| size = (willUnpackImage || RemoteMatchesUnpackedImg(selectedImagePath)) ? remote.extractedSize : remote.compressedSize; | |
| m_selectedFileSize = remote.compressedSize; | |
| } | |
| // add the installer size if this is not a live image | |
| if (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| size += INSTALLER_DELTA_SIZE + m_installerImage.extractedSize; | |
| } | |
| m_finalSize = size; | |
| // update Thank You page fields with the selected image data | |
| uint32_t headlineMsg; | |
| switch (m_selectedInstallMethod) { | |
| case InstallDualBoot: | |
| headlineMsg = MSG_320; | |
| break; | |
| case ReformatterUsb: | |
| headlineMsg = MSG_344; | |
| break; | |
| case LiveUsb: | |
| case CombinedUsb: | |
| headlineMsg = MSG_343; | |
| break; | |
| default: | |
| uprintf("Unexpected install method %ls", InstallMethodToStr(m_selectedInstallMethod)); | |
| headlineMsg = MSG_343; | |
| } | |
| CString finalMessageStr = UTF8ToCString(lmprintf(headlineMsg)); | |
| CString imageLanguage = UTF8ToCString(lmprintf(m_personalityToLocaleMsg[personality])); | |
| CStringA imageTypeA = lmprintf(personality == PERSONALITY_BASE ? MSG_400 : MSG_316); // Basic or Full | |
| CString imageType = UTF8ToCString(imageTypeA); | |
| SetElementText(_T(ELEMENT_THANKYOU_MESSAGE), CComBSTR(finalMessageStr)); | |
| SetElementText(_T(ELEMENT_INSTALLER_VERSION), CComBSTR(version)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_INSTALLER_LANGUAGE_ROW), CComVariant(personality != PERSONALITY_BASE)); | |
| SetElementText(_T(ELEMENT_INSTALLER_LANGUAGE), CComBSTR(imageLanguage)); | |
| CString contentStr = UTF8ToCString(lmprintf(MSG_319, imageTypeA, SizeToHumanReadable(size, FALSE, use_fake_units))); | |
| SetElementText(_T(ELEMENT_INSTALLER_CONTENT), CComBSTR(contentStr)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_DUALBOOT_REMINDER), CComVariant(m_selectedInstallMethod == InstallMethod_t::InstallDualBoot)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_LIVE_REMINDER), CComVariant(m_selectedInstallMethod == InstallMethod_t::LiveUsb || m_selectedInstallMethod == InstallMethod_t::CombinedUsb)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_REFLASHER_REMINDER), CComVariant(m_selectedInstallMethod == InstallMethod_t::ReformatterUsb)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_USBBOOT_HOWTO), CComVariant(m_selectedInstallMethod != InstallMethod_t::InstallDualBoot)); | |
| if (m_selectedInstallMethod != InstallMethod_t::InstallDualBoot) { | |
| CallJavascript(_T(JS_RESET_CHECK), CComVariant(_T(ELEMENT_SELUSB_AGREEMENT))); | |
| m_usbDeleteAgreement = false; | |
| // RADU: move this to another thread | |
| GetUSBDevices(0); | |
| OnSelectedUSBDiskChanged(NULL); | |
| PF_INIT(SHChangeNotifyRegister, shell32); | |
| // Register MEDIA_INSERTED/MEDIA_REMOVED notifications for card readers | |
| if (pfSHChangeNotifyRegister && SUCCEEDED(SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, &pidlDesktop))) { | |
| NotifyEntry.pidl = pidlDesktop; | |
| NotifyEntry.fRecursive = TRUE; | |
| // NB: The following only works if the media is already formatted. | |
| // If you insert a blank card, notifications will not be sent... :( | |
| m_shellNotificationsRegister = pfSHChangeNotifyRegister(m_hWnd, 0x0001 | 0x0002 | 0x8000, | |
| SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED, UM_MEDIA_CHANGE, 1, &NotifyEntry); | |
| } | |
| } | |
| if (m_selectedInstallMethod != InstallMethod_t::InstallDualBoot) { | |
| CString displayName; | |
| GetImgDisplayName(displayName, version, personality, 0); | |
| SetElementText(_T(ELEMENT_SELUSB_NEW_DISK_NAME), CComBSTR(displayName)); | |
| CString selectedSize = UTF8ToCString(SizeToHumanReadable(size, FALSE, use_fake_units)); | |
| SetElementText(_T(ELEMENT_SELUSB_NEW_DISK_SIZE), CComBSTR(selectedSize)); | |
| ChangePage(_T(ELEMENT_USB_PAGE)); | |
| } else { | |
| GoToSelectStoragePage(); | |
| } | |
| CString imageSource = _T("Remote"); | |
| if (m_useLocalFile) imageSource = _T("Local"); | |
| TrackEvent(_T("ImageSource"), imageSource); | |
| TrackEvent(_T("Version"), version); | |
| TrackEvent(_T("Personality"), personality); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectedImageFileChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| CComBSTR selectedValue; | |
| HRESULT hr = pElement->QueryInterface(IID_IHTMLSelectElement, (void**)&selectElement); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && selectElement != NULL, "Error querying for IHTMLSelectElement interface", S_OK); | |
| hr = selectElement->get_value(&selectedValue); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && selectElement != NULL, "Error getting selected file value", S_OK); | |
| safe_free(image_path); | |
| image_path = wchar_to_utf8(selectedValue); | |
| uprintf("OnSelectedImageFileChanged to LOCAL [%s]", image_path); | |
| m_useLocalFile = true; | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectedRemoteFileChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| long selectedIndex; | |
| HRESULT hr = pElement->QueryInterface(IID_IHTMLSelectElement, (void**)&selectElement); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && selectElement != NULL, "Error querying for IHTMLSelectElement interface", S_OK); | |
| hr = selectElement->get_selectedIndex(&selectedIndex); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "Error getting selected index value", S_OK); | |
| m_selectedRemoteIndex = selectedIndex; | |
| POSITION p = m_remoteImages.FindIndex(selectedIndex); | |
| IFFALSE_RETURN_VALUE(p != NULL, "Index value not valid.", S_OK); | |
| RemoteImageEntry_t r = m_remoteImages.GetAt(p); | |
| uprintf("OnSelectedRemoteFileChanged to REMOTE [%ls]", r.displayName); | |
| if (r.personality != PERSONALITY_BASE) { | |
| SetElementText(_T(ELEMENT_DOWNLOAD_FULL_SIZE), GetDownloadString(r)); | |
| } | |
| m_useLocalFile = false; | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectedImageTypeChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComBSTR id; | |
| HRESULT hr; | |
| CComPtr<IHTMLElement> selectElem; | |
| bool localFileSelected = false; | |
| hr = pElement->get_id(&id); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "OnSelectedImageTypeChanged: Error getting element id", S_OK); | |
| if (id == _T(ELEMENT_IMAGE_TYPE_LOCAL)) { | |
| hr = GetElement(_T(ELEMENT_FILES_SELECT), &selectElem); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "OnSelectedImageTypeChanged: querying for local select element.", S_OK); | |
| OnSelectedImageFileChanged(selectElem); | |
| } else { | |
| hr = GetElement(_T(ELEMENT_REMOTE_SELECT), &selectElem); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "OnSelectedImageTypeChanged: querying for local select element.", S_OK); | |
| OnSelectedRemoteFileChanged(selectElem); | |
| } | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnDownloadLightButtonClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| m_selectedRemoteIndex = m_baseImageRemoteIndex; | |
| m_useLocalFile = false; | |
| OnSelectFileNextClicked(pElement); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnDownloadFullButtonClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| // trigger select onchange to update selected index | |
| CComPtr<IHTMLElement> pSelElem; | |
| GetElement(_T(ELEMENT_SELFILE_DOWN_LANG), &pSelElem); | |
| CComQIPtr<IHTMLElement3> spElem3(pSelElem); | |
| CComPtr<IHTMLEventObj> spEo; | |
| CComQIPtr<IHTMLDocument4> spDoc4(m_spHtmlDoc); // pDoc Document | |
| spDoc4->createEventObject(NULL, &spEo); | |
| CComQIPtr<IDispatch> spDisp(spEo); | |
| CComVariant var(spDisp); | |
| VARIANT_BOOL bCancel = VARIANT_FALSE; | |
| spElem3->fireEvent(L"onchange", &var, &bCancel); | |
| m_useLocalFile = false; | |
| OnSelectFileNextClicked(pElement); | |
| return S_OK; | |
| } | |
| // Select USB Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnSelectUSBPreviousClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| LeavingDevicesPage(); | |
| ChangePage(_T(ELEMENT_FILE_PAGE)); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectUSBNextClicked(IHTMLElement* pElement) | |
| { | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnSelectUSBNextClicked: Button is disabled. ", S_OK); | |
| FUNCTION_ENTER; | |
| TrackEvent(_T("USBSizeMB"), SelectedDrive.DiskSize / BYTES_IN_MEGABYTE); | |
| LeavingDevicesPage(); | |
| StartInstallationProcess(); | |
| return S_OK; | |
| } | |
| void CEndlessUsbToolDlg::StartInstallationProcess() | |
| { | |
| SetElementText(_T(ELEMENT_INSTALL_STATUS), CComBSTR("")); | |
| TrackEvent(_T("Started")); | |
| EnableHibernate(false); | |
| if(m_selectedInstallMethod != InstallMethod_t::InstallDualBoot) ChangeDriveAutoRunAndMount(true); | |
| if (IsDualBootOrCombinedUsb() && m_useLocalFile) { | |
| pFileImageEntry_t localEntry = NULL; | |
| CString selectedImage = UTF8ToCString(image_path); | |
| if (m_imageFiles.Lookup(selectedImage, localEntry) && localEntry->hasBootArchive && localEntry->hasBootArchiveSig && localEntry->hasUnpackedImgSig) { | |
| m_bootArchive = localEntry->bootArchivePath; | |
| m_bootArchiveSig = localEntry->bootArchiveSigPath; | |
| m_unpackedImageSig = localEntry->unpackedImgSigPath; | |
| } else { | |
| m_useLocalFile = false; | |
| } | |
| } | |
| // Radu: we need to download an installer if only a live image is found and full install was selected | |
| if (m_useLocalFile) { | |
| if (IsDualBootOrCombinedUsb()) { | |
| m_localFile = m_bootArchive; | |
| m_localFileSig = m_bootArchiveSig; | |
| } else { | |
| m_localFile = UTF8ToCString(image_path); | |
| if (!GetSignatureForLocalFile(m_localFile, m_localFileSig)) { | |
| ChangePage(_T(ELEMENT_INSTALL_PAGE)); | |
| uprintf("Error: couldn't find local entry for selected local file '%ls'", m_localFile); | |
| FormatStatus = FORMAT_STATUS_CANCEL; | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseGeneric; | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| return; | |
| } | |
| } | |
| StartOperationThread(OP_VERIFYING_SIGNATURE, CEndlessUsbToolDlg::FileVerificationThread); | |
| } else { | |
| UpdateCurrentStep(OP_DOWNLOADING_FILES); | |
| RemoteImageEntry_t remote = m_remoteImages.GetAt(m_remoteImages.FindIndex(m_selectedRemoteIndex)); | |
| DownloadType_t downloadType = GetSelectedDownloadType(); | |
| // live image file | |
| CString url = CString(RELEASE_JSON_URLPATH) + remote.urlFile; | |
| m_localFile = GET_IMAGE_PATH(CSTRING_GET_LAST(remote.urlFile, '/')); | |
| bool localFileExists = PackedImageAlreadyExists(m_localFile, remote.compressedSize, remote.extractedSize, false); | |
| // live image signature file | |
| CString urlAsc = CString(RELEASE_JSON_URLPATH) + remote.urlSignature; | |
| if(RemoteMatchesUnpackedImg(m_localFile, &m_localFileSig)) { | |
| m_localFile = GET_IMAGE_PATH(ENDLESS_IMG_FILE_NAME); | |
| urlAsc = CString(RELEASE_JSON_URLPATH) + remote.urlUnpackedSignature; | |
| } else { | |
| m_localFileSig = GET_IMAGE_PATH(CSTRING_GET_LAST(remote.urlSignature, '/')); | |
| } | |
| // add image file path for Rufus | |
| safe_free(image_path); | |
| image_path = wchar_to_utf8(m_localFile); | |
| // List of files to download | |
| ListOfStrings urls, files; | |
| CString urlInstaller, urlInstallerAsc, installerFile, installerAscFile; | |
| CString urlBootFiles, urlBootFilesAsc, urlImageSig; | |
| if (IsDualBootOrCombinedUsb()) { | |
| urlBootFiles = CString(RELEASE_JSON_URLPATH) + remote.urlBootArchive; | |
| m_bootArchive = GET_IMAGE_PATH(CSTRING_GET_LAST(urlBootFiles, '/')); | |
| urlBootFilesAsc = CString(RELEASE_JSON_URLPATH) + remote.urlBootArchiveSignature; | |
| m_bootArchiveSig = GET_IMAGE_PATH(CSTRING_GET_LAST(urlBootFilesAsc, '/')); | |
| urlImageSig = CString(RELEASE_JSON_URLPATH) + remote.urlUnpackedSignature; | |
| m_unpackedImageSig = GET_IMAGE_PATH(CSTRING_GET_LAST(urlImageSig, '/')); | |
| if (localFileExists) { | |
| urls = { urlAsc, urlBootFiles, urlBootFilesAsc, urlImageSig }; | |
| files = { m_localFileSig, m_bootArchive, m_bootArchiveSig, m_unpackedImageSig }; | |
| } else { | |
| urls = { url, urlAsc, urlBootFiles, urlBootFilesAsc, urlImageSig }; | |
| files = { m_localFile, m_localFileSig, m_bootArchive, m_bootArchiveSig, m_unpackedImageSig }; | |
| } | |
| } else if (m_selectedInstallMethod == InstallMethod_t::LiveUsb) { | |
| if (localFileExists) { | |
| urls = { urlAsc }; | |
| files = { m_localFileSig }; | |
| } else { | |
| urls = { url, urlAsc }; | |
| files = { m_localFile, m_localFileSig }; | |
| } | |
| } else { | |
| // installer image file + signature | |
| urlInstaller = CString(RELEASE_JSON_URLPATH) + m_installerImage.urlFile; | |
| installerFile = GET_IMAGE_PATH(CSTRING_GET_LAST(m_installerImage.urlFile, '/')); | |
| bool installerFileExists = PackedImageAlreadyExists(installerFile, m_installerImage.compressedSize, m_installerImage.extractedSize, true); | |
| urlInstallerAsc = CString(RELEASE_JSON_URLPATH) + m_installerImage.urlSignature; | |
| installerAscFile = GET_IMAGE_PATH(CSTRING_GET_LAST(m_installerImage.urlSignature, '/')); | |
| if (installerFileExists) { | |
| if (localFileExists) { | |
| urls = { urlAsc, urlInstallerAsc }; | |
| files = { m_localFileSig, installerAscFile }; | |
| } else { | |
| urls = { url, urlAsc, urlInstallerAsc }; | |
| files = { m_localFile, m_localFileSig, installerAscFile }; | |
| } | |
| } else if(localFileExists) { | |
| urls = { urlAsc, urlInstaller, urlInstallerAsc }; | |
| files = { m_localFileSig, installerFile, installerAscFile }; | |
| } else { | |
| urls = { url, urlAsc, urlInstaller, urlInstallerAsc }; | |
| files = { m_localFile, m_localFileSig, installerFile, installerAscFile }; | |
| } | |
| } | |
| // Try resuming the download if it exists | |
| bool status = m_downloadManager.AddDownload(downloadType, urls, files, true, remote.downloadJobName); | |
| if (!status) { | |
| // start the download again | |
| status = m_downloadManager.AddDownload(downloadType, urls, files, false, remote.downloadJobName); | |
| if (!status) { | |
| ChangePage(_T(ELEMENT_INSTALL_PAGE)); | |
| // RADU: add custom error values for each of the errors so we can identify them and show a custom message for each | |
| uprintf("Error adding files for download"); | |
| FormatStatus = FORMAT_STATUS_CANCEL; | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseDownloadFailed; | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| return; | |
| } | |
| } | |
| // Create a thread to poll the download progress regularly as the event based BITS one is very irregular | |
| if (m_downloadUpdateThread == INVALID_HANDLE_VALUE) { | |
| m_downloadUpdateThread = CreateThread(NULL, 0, CEndlessUsbToolDlg::UpdateDownloadProgressThread, (LPVOID)this, 0, NULL); | |
| } | |
| // add remote installer data to local installer data | |
| m_localInstallerImage.stillPresent = TRUE; | |
| m_localInstallerImage.filePath = GET_IMAGE_PATH(CSTRING_GET_LAST(m_installerImage.urlFile, '/')); | |
| m_localInstallerImage.size = m_installerImage.compressedSize; | |
| if (IsDualBootOrCombinedUsb()) { | |
| m_localFile = m_bootArchive; | |
| m_localFileSig = m_bootArchiveSig; | |
| } | |
| } | |
| ChangePage(_T(ELEMENT_INSTALL_PAGE)); | |
| // RADU: wait for File scanning thread | |
| if (m_fileScanThread != INVALID_HANDLE_VALUE) { | |
| SetEvent(m_closeFileScanThreadEvent); | |
| uprintf("Waiting for scan files thread."); | |
| WaitForSingleObject(m_fileScanThread, 5000); | |
| m_fileScanThread = INVALID_HANDLE_VALUE; | |
| CloseHandle(m_closeFileScanThreadEvent); | |
| m_closeFileScanThreadEvent = INVALID_HANDLE_VALUE; | |
| } | |
| } | |
| void CEndlessUsbToolDlg::StartOperationThread(int operation, LPTHREAD_START_ROUTINE threadRoutine, LPVOID param) | |
| { | |
| FUNCTION_ENTER; | |
| if (m_operationThread != INVALID_HANDLE_VALUE) { | |
| uprintf("StartThread: Another operation is in progress. Current operation %ls(%d), requested operation %ls(%d)", | |
| OperationToStr(m_currentStep), m_currentStep, OperationToStr(operation), operation); | |
| return; | |
| } | |
| UpdateCurrentStep(operation); | |
| FormatStatus = 0; | |
| m_operationThread = CreateThread(NULL, 0, threadRoutine, param != NULL ? param : (LPVOID)this, 0, NULL); | |
| if (m_operationThread == NULL) { | |
| m_operationThread = INVALID_HANDLE_VALUE; | |
| uprintf("Error: Unable to start thread."); | |
| FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD); | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseGeneric; | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, (WPARAM)FALSE, 0); | |
| } | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectedUSBDiskChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| //int i; | |
| char fs_type[32]; | |
| int deviceIndex = ComboBox_GetCurSel(hDeviceList); | |
| UpdateUSBSpeedMessage(deviceIndex); | |
| IFFALSE_GOTOERROR(deviceIndex >= 0, "Selected drive index is invalid."); | |
| memset(&SelectedDrive, 0, sizeof(SelectedDrive)); | |
| SelectedDrive.DeviceNumber = (DWORD)ComboBox_GetItemData(hDeviceList, deviceIndex); | |
| GetDrivePartitionData(SelectedDrive.DeviceNumber, fs_type, sizeof(fs_type), FALSE); | |
| if (pElement != NULL) { | |
| CallJavascript(_T(JS_RESET_CHECK), CComVariant(_T(ELEMENT_SELUSB_AGREEMENT))); | |
| m_usbDeleteAgreement = false; | |
| } | |
| // RADU: should it be >= or should we take some more stuff into account like the partition size | |
| BOOL isDiskBigEnough = (ULONGLONG)SelectedDrive.DiskSize > m_finalSize; | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_SELUSB_NEXT_BUTTON))), CComVariant(m_usbDeleteAgreement && isDiskBigEnough)); | |
| CallJavascript(_T(JS_ENABLE_ELEMENT), CComVariant(_T(ELEMENT_SELUSB_USB_DRIVES)), CComVariant(TRUE)); | |
| return S_OK; | |
| error: | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_SELUSB_NEXT_BUTTON))), CComVariant(FALSE)); | |
| CallJavascript(_T(JS_ENABLE_ELEMENT), CComVariant(_T(ELEMENT_SELUSB_USB_DRIVES)), CComVariant(FALSE)); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnAgreementCheckboxChanged(IHTMLElement *pElement) | |
| { | |
| FUNCTION_ENTER; | |
| m_usbDeleteAgreement = !m_usbDeleteAgreement; | |
| OnSelectedUSBDiskChanged(NULL); | |
| return S_OK; | |
| } | |
| void CEndlessUsbToolDlg::LeavingDevicesPage() | |
| { | |
| FUNCTION_ENTER; | |
| PF_INIT(SHChangeNotifyDeregister, Shell32); | |
| if (pfSHChangeNotifyDeregister != NULL && m_shellNotificationsRegister != 0) { | |
| pfSHChangeNotifyDeregister(m_shellNotificationsRegister); | |
| m_shellNotificationsRegister = 0; | |
| } | |
| } | |
| // Select Storage Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnSelectStoragePreviousClicked(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| ChangePage(_T(ELEMENT_FILE_PAGE)); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectStorageNextClicked(IHTMLElement *pElement) | |
| { | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnSelectStorageNextClicked: Button is disabled. ", S_OK); | |
| FUNCTION_ENTER; | |
| TrackEvent(_T("StorageSizeGB"), (LONGLONG) m_nrGigsSelected); | |
| StartInstallationProcess(); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnSelectedStorageSizeChanged(IHTMLElement* pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| CComBSTR selectedValue; | |
| HRESULT hr = pElement->QueryInterface(IID_IHTMLSelectElement, (void**)&selectElement); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && selectElement != NULL, "Error querying for IHTMLSelectElement interface", S_OK); | |
| hr = selectElement->get_value(&selectedValue); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && selectElement != NULL, "Error getting selected file value", S_OK); | |
| m_nrGigsSelected = _wtoi(selectedValue); | |
| uprintf("Number of Gb selected for the endless OS file: %d", m_nrGigsSelected); | |
| return S_OK; | |
| } | |
| #define MIN_NO_OF_GIGS 8 | |
| #define MAX_NO_OF_GIGS 256 | |
| #define RECOMMENDED_GIGS_BASE 16 | |
| #define RECOMMENDED_GIGS_FULL 32 | |
| #define IS_MINIMUM_VALUE 1 | |
| #define IS_MAXIMUM_VALUE 2 | |
| #define IS_BASE_IMAGE 3 | |
| ULONGLONG CEndlessUsbToolDlg::GetNeededSpaceForDualBoot(int &neededGigs, bool *isBaseImage) | |
| { | |
| if(isBaseImage != NULL) *isBaseImage = true; | |
| // figure out how much space we need | |
| ULONGLONG neededSize = 0; | |
| ULONGLONG bytesInGig = BYTES_IN_GIGABYTE; | |
| if (m_useLocalFile) { | |
| pFileImageEntry_t localEntry = NULL; | |
| CString selectedImage = UTF8ToCString(image_path); | |
| if (!m_imageFiles.Lookup(selectedImage, localEntry)) { | |
| uprintf("ERROR: Selected local file not found."); | |
| } else { | |
| if (isBaseImage != NULL) *isBaseImage = (localEntry->personality == PERSONALITY_BASE); | |
| neededSize = localEntry->isUnpackedImage ? localEntry->size : GetExtractedSize(selectedImage, FALSE); | |
| } | |
| } else { | |
| POSITION p = m_remoteImages.FindIndex(m_selectedRemoteIndex); | |
| if (p != NULL) { | |
| RemoteImageEntry_t remote = m_remoteImages.GetAt(p); | |
| neededSize = remote.extractedSize; | |
| if (GetExePath().Left(3) == GetSystemDrive()) { | |
| neededSize += remote.compressedSize + remote.bootArchiveSize; | |
| } | |
| if (isBaseImage != NULL) *isBaseImage = (remote.personality == PERSONALITY_BASE); | |
| } | |
| } | |
| neededSize += bytesInGig; | |
| neededGigs = (int)(neededSize / bytesInGig); | |
| return neededSize; | |
| } | |
| void CEndlessUsbToolDlg::GoToSelectStoragePage() | |
| { | |
| ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes; | |
| CComPtr<IHTMLSelectElement> selectElement; | |
| HRESULT hr; | |
| int maxAvailableGigs = 0; | |
| CStringA freeSize, totalSize; | |
| bool isBaseImage = true; | |
| ULONGLONG bytesInGig = BYTES_IN_GIGABYTE; | |
| int neededGigs = 0; | |
| ULONGLONG neededSize = GetNeededSpaceForDualBoot(neededGigs, &isBaseImage); | |
| CStringW systemDrive = GetSystemDrive(); | |
| CStringA systemDriveA = ConvertUnicodeToUTF8(systemDrive); | |
| // get available space on system drive | |
| IFFALSE_RETURN(GetDiskFreeSpaceEx(systemDrive, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes) != 0, "Error on GetDiskFreeSpace"); | |
| totalSize = SizeToHumanReadable(totalNumberOfBytes.QuadPart, FALSE, use_fake_units); | |
| freeSize = SizeToHumanReadable(freeBytesAvailable.QuadPart, FALSE, use_fake_units); | |
| uprintf("Available space on drive %s is %s out of %s; we need %s", systemDrive, freeSize, totalSize, SizeToHumanReadable(neededSize, FALSE, use_fake_units)); | |
| maxAvailableGigs = (int) ((freeBytesAvailable.QuadPart - bytesInGig) / bytesInGig); | |
| // update messages with needed space based on selected version | |
| CStringA osVersion = lmprintf(isBaseImage ? MSG_400 : MSG_316); | |
| CStringA osSizeText = SizeToHumanReadable((isBaseImage ? RECOMMENDED_GIGS_BASE : RECOMMENDED_GIGS_FULL) * bytesInGig, FALSE, use_fake_units); | |
| CString message = UTF8ToCString(lmprintf(MSG_337, osVersion, osSizeText)); | |
| SetElementText(_T(ELEMENT_STORAGE_DESCRIPTION), CComBSTR(message)); | |
| message = UTF8ToCString(lmprintf(MSG_341, freeSize, totalSize, systemDriveA)); | |
| SetElementText(_T(ELEMENT_STORAGE_AVAILABLE), CComBSTR(message)); | |
| message = UTF8ToCString(lmprintf(MSG_342, systemDriveA)); | |
| SetElementText(_T(ELEMENT_STORAGE_MESSAGE), CComBSTR(message)); | |
| // Clear existing elements from the drop down | |
| hr = GetSelectElement(_T(ELEMENT_STORAGE_SELECT), selectElement); | |
| IFFALSE_RETURN(SUCCEEDED(hr) && selectElement != NULL, "Error returned from GetSelectElement."); | |
| hr = selectElement->put_length(0); | |
| bool enoughBytesAvailable = (freeBytesAvailable.QuadPart - bytesInGig) > neededSize; | |
| // Enable/disable ui elements based on space availability | |
| CallJavascript(_T(JS_ENABLE_ELEMENT), CComVariant(_T(ELEMENT_STORAGE_SELECT)), CComVariant(enoughBytesAvailable)); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_SELSTORAGE_NEXT_BUTTON))), CComVariant(enoughBytesAvailable)); | |
| // Show/hide space warning vs "back up your files!" notice | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(_T(ELEMENT_STORAGE_AGREEMENT_TEXT)), CComVariant(enoughBytesAvailable)); | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(_T(ELEMENT_STORAGE_SPACE_WARNING)), CComVariant(!enoughBytesAvailable)); | |
| if (!enoughBytesAvailable) { | |
| uprintf("Not enough bytes available."); | |
| message = UTF8ToCString(lmprintf(MSG_335, SizeToHumanReadable(neededSize, FALSE, use_fake_units), freeSize, systemDriveA)); | |
| SetElementText(_T(ELEMENT_STORAGE_SPACE_WARNING), CComBSTR(message)); | |
| ChangePage(_T(ELEMENT_STORAGE_PAGE)); | |
| TrackEvent(_T("StorageInsufficient")); | |
| return; | |
| } | |
| // Add the entries | |
| m_nrGigsSelected = neededGigs; | |
| IFFALSE_RETURN(AddStorageEntryToSelect(selectElement, neededGigs, IS_MINIMUM_VALUE), "Error adding value to select."); | |
| for (int nrGigs = MIN_NO_OF_GIGS; nrGigs < min(maxAvailableGigs, MAX_NO_OF_GIGS); nrGigs = nrGigs * 2) { | |
| IFFALSE_CONTINUE(nrGigs > neededGigs, ""); | |
| IFFALSE_RETURN(AddStorageEntryToSelect(selectElement, nrGigs, isBaseImage ? IS_BASE_IMAGE : 0), "Error adding value to select."); | |
| } | |
| IFFALSE_RETURN(AddStorageEntryToSelect(selectElement, maxAvailableGigs, IS_MAXIMUM_VALUE), "Error adding value to select."); | |
| ChangePage(_T(ELEMENT_STORAGE_PAGE)); | |
| } | |
| BOOL CEndlessUsbToolDlg::AddStorageEntryToSelect(CComPtr<IHTMLSelectElement> &selectElement, int noOfGigs, uint8_t extraData) | |
| { | |
| ULONGLONG size = noOfGigs; | |
| size = size * BYTES_IN_GIGABYTE; | |
| CStringA sizeText = SizeToHumanReadable(size, FALSE, use_fake_units); | |
| CString value, text; | |
| int locMsg = 0; | |
| value.Format(L"%d", noOfGigs); | |
| if (extraData == IS_MINIMUM_VALUE) locMsg = MSG_338; | |
| else if (extraData == IS_MAXIMUM_VALUE) locMsg = MSG_340; | |
| else if (extraData == IS_BASE_IMAGE && noOfGigs == RECOMMENDED_GIGS_BASE) locMsg = MSG_339; | |
| else if (extraData == 0 && noOfGigs == RECOMMENDED_GIGS_FULL) locMsg = MSG_339; | |
| if (locMsg != 0) { | |
| text = UTF8ToCString(lmprintf(locMsg, sizeText)); | |
| } else { | |
| text = UTF8ToCString(sizeText); | |
| } | |
| if (locMsg == MSG_339) { | |
| m_nrGigsSelected = noOfGigs; | |
| } | |
| return SUCCEEDED(AddEntryToSelect(selectElement, CComBSTR(value), CComBSTR(text), NULL, locMsg == MSG_339)); | |
| } | |
| // Install Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnInstallCancelClicked(IHTMLElement* pElement) | |
| { | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "OnInstallCancelClicked: Button is disabled. ", S_OK); | |
| FUNCTION_ENTER; | |
| if (!CancelInstall()) { | |
| return S_OK; | |
| } | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseCancelled; | |
| CancelRunningOperation(true); | |
| return S_OK; | |
| } | |
| // Error/Thank You Page Handlers | |
| HRESULT CEndlessUsbToolDlg::OnCloseAppClicked(IHTMLElement* pElement) | |
| { | |
| EndDialog(IDCLOSE); | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnRecoverErrorButtonClicked(IHTMLElement* pElement) | |
| { | |
| ErrorCause_t errorCause = m_lastErrorCause; | |
| uprintf("OnRecoverErrorButtonClicked error cause %ls(%d)", ErrorCauseToStr(errorCause), errorCause); | |
| IFFALSE_RETURN_VALUE(!IsButtonDisabled(pElement), "Button is disabled. User didn't check the 'Delete invalid files' checkbox.", S_OK); | |
| // reset the state | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseNone; | |
| m_localFilesScanned = false; | |
| SetJSONDownloadState(JSONDownloadState::Pending); | |
| ResetEvent(m_cancelOperationEvent); | |
| ResetEvent(m_closeFileScanThreadEvent); | |
| StartCheckInternetConnectionThread(); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_INSTALL_CANCEL))), CComVariant(TRUE)); | |
| // continue based on error cause | |
| switch (errorCause) { | |
| case ErrorCause_t::ErrorCauseDownloadFailed: | |
| case ErrorCause_t::ErrorCauseDownloadFailedDiskFull: | |
| StartInstallationProcess(); | |
| break; | |
| case ErrorCause_t::ErrorCauseWriteFailed: | |
| if (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| safe_free(image_path); | |
| image_path = wchar_to_utf8(m_LiveFile); | |
| } | |
| OnSelectFileNextClicked(NULL); | |
| break; | |
| case ErrorCause_t::ErrorCauseVerificationFailed: | |
| { | |
| BOOL result = DeleteFile(m_localFile); | |
| uprintf("%s on deleting file '%ls' - %s", result ? "Success" : "Error", m_localFile, WindowsErrorString()); | |
| SetLastError(ERROR_SUCCESS); | |
| result = DeleteFile(m_localFileSig); | |
| uprintf("%s on deleting file '%ls' - %s", result ? "Success" : "Error", m_localFileSig, WindowsErrorString()); | |
| ChangePage(_T(ELEMENT_DUALBOOT_PAGE)); | |
| break; | |
| } | |
| case ErrorCause_t::ErrorCauseCancelled: | |
| default: | |
| ChangePage(_T(ELEMENT_DUALBOOT_PAGE)); | |
| break; | |
| } | |
| if (m_taskbarProgress != NULL) { | |
| m_taskbarProgress->SetProgressState(m_hWnd, TBPF_NORMAL); | |
| m_taskbarProgress->SetProgressValue(m_hWnd, 0, 100); | |
| } | |
| return S_OK; | |
| } | |
| HRESULT CEndlessUsbToolDlg::OnDeleteCheckboxChanged(IHTMLElement *pElement) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IHTMLOptionButtonElement> checkboxElem; | |
| VARIANT_BOOL checked = VARIANT_FALSE; | |
| HRESULT hr = pElement->QueryInterface(&checkboxElem); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && checkboxElem != NULL, "Error querying for IHTMLOptionButtonElement.", S_OK); | |
| hr = checkboxElem->get_checked(&checked); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "Error querying for IHTMLOptionButtonElement.", S_OK); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_ERROR_BUTTON))), checked); | |
| return S_OK; | |
| } | |
| bool CEndlessUsbToolDlg::CancelInstall() | |
| { | |
| bool operation_in_progress = m_currentStep == OP_FLASHING_DEVICE; | |
| uprintf("Cancel requested. Operation in progress: %s", operation_in_progress ? "YES" : "NO"); | |
| if (operation_in_progress) { | |
| if (FormatStatus != FORMAT_STATUS_CANCEL) { | |
| int result = MessageBoxExU(hMainDialog, lmprintf(MSG_105), lmprintf(MSG_049), MB_YESNO | MB_ICONWARNING | MB_IS_RTL, selected_langid); | |
| if (result == IDYES) { | |
| uprintf("Cancel operation confirmed."); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_INSTALL_CANCEL))), CComVariant(FALSE)); | |
| FormatStatus = FORMAT_STATUS_CANCEL; | |
| m_cancelImageUnpack = 1; | |
| m_lastErrorCause = ErrorCause_t::ErrorCauseCancelled; | |
| uprintf("Cancelling current operation."); | |
| CString str = UTF8ToCString(lmprintf(MSG_201)); | |
| SetElementText(_T(ELEMENT_INSTALL_STATUS), CComBSTR("")); | |
| } | |
| } | |
| } | |
| return !operation_in_progress; | |
| } | |
| DownloadType_t CEndlessUsbToolDlg::GetSelectedDownloadType() | |
| { | |
| if (IsDualBootOrCombinedUsb()) { | |
| return DownloadType_t::DownloadTypeDualBootFiles; | |
| } else { | |
| return m_selectedInstallMethod == InstallMethod_t::LiveUsb ? DownloadType_t::DownloadTypeLiveImage : DownloadType_t::DownloadTypeInstallerImage; | |
| } | |
| } | |
| void CEndlessUsbToolDlg::OnClose() | |
| { | |
| FUNCTION_ENTER; | |
| if (!CancelInstall()) { | |
| m_closeRequested = true; | |
| return; | |
| } | |
| CDHtmlDialog::OnClose(); | |
| } | |
| HRESULT CEndlessUsbToolDlg::CallJavascript(LPCTSTR method, CComVariant parameter1, CComVariant parameter2) | |
| { | |
| HRESULT hr; | |
| //uprintf("CallJavascript called with method %ls", method); | |
| if (m_spWindowElem == NULL) { | |
| hr = m_spHtmlDoc->get_parentWindow(&m_spWindowElem); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && m_spWindowElem != NULL, "Error querying for parent window.", E_FAIL); | |
| } | |
| if (m_dispWindow == NULL) { | |
| hr = m_spWindowElem->QueryInterface(&m_dispWindow); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && m_dispWindow != NULL, "Error querying for CComDispatchDriver.", E_FAIL); | |
| } | |
| if (m_dispexWindow == NULL) { | |
| hr = m_spWindowElem->QueryInterface(&m_dispexWindow); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr) && m_dispexWindow != NULL, "Error querying for IDispatchEx.", E_FAIL); | |
| } | |
| DISPID dispidMethod = -1; | |
| hr = m_dispexWindow->GetDispID(CComBSTR(method), fdexNameCaseSensitive, &dispidMethod); | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "Error getting method dispid", E_FAIL); | |
| if (parameter2 == CComVariant()) { | |
| hr = m_dispWindow.Invoke1(dispidMethod, ¶meter1); | |
| } else { | |
| hr = m_dispWindow.Invoke2(dispidMethod, ¶meter1, ¶meter2); | |
| } | |
| IFFALSE_RETURN_VALUE(SUCCEEDED(hr), "Error when calling method.", E_FAIL); | |
| return S_OK; | |
| } | |
| void CEndlessUsbToolDlg::UpdateCurrentStep(int currentStep) | |
| { | |
| if (m_currentStep == currentStep) { | |
| uprintf("Already at step %ls(%d)", OperationToStr(currentStep), currentStep); | |
| return; | |
| } | |
| FUNCTION_ENTER; | |
| int nrSteps = m_useLocalFile ? 2 : 3; | |
| CString action; | |
| int nrCurrentStep; | |
| int locMsgIdTitle; | |
| m_currentStep = currentStep; | |
| switch (m_currentStep) | |
| { | |
| case OP_DOWNLOADING_FILES: | |
| action = _T("DownloadStarted"); | |
| locMsgIdTitle = MSG_304; | |
| nrCurrentStep = 1; | |
| break; | |
| case OP_VERIFYING_SIGNATURE: | |
| action = _T("VerifyStarted"); | |
| locMsgIdTitle = MSG_310; | |
| nrCurrentStep = m_useLocalFile ? 1 : 2; | |
| break; | |
| case OP_FLASHING_DEVICE: | |
| case OP_FILE_COPY: | |
| case OP_NEW_LIVE_CREATION: | |
| action = _T("WritingStarted"); | |
| locMsgIdTitle = MSG_311; | |
| nrCurrentStep = m_useLocalFile ? 2 : 3; | |
| break; | |
| case OP_SETUP_DUALBOOT: | |
| action = _T("InstallStarted"); | |
| locMsgIdTitle = MSG_309; | |
| nrCurrentStep = m_useLocalFile ? 2 : 3; | |
| break; | |
| default: | |
| action.Format(_T("%ls(%d)"), OperationToStr(currentStep), currentStep); | |
| uprintf("Unknown operation %ls", action); | |
| break; | |
| } | |
| CString str; | |
| str = UTF8ToCString(lmprintf(MSG_305, nrCurrentStep)); | |
| SetElementText(_T(ELEMENT_INSTALL_STEP), CComBSTR(str)); | |
| str = UTF8ToCString(lmprintf(MSG_306, nrSteps)); | |
| SetElementText(_T(ELEMENT_INSTALL_STEPS_TOTAL), CComBSTR(str)); | |
| str = UTF8ToCString(lmprintf(locMsgIdTitle)); | |
| SetElementText(_T(ELEMENT_INSTALL_STEP_TEXT), CComBSTR(str)); | |
| CallJavascript(_T(JS_SET_PROGRESS), CComVariant(0)); | |
| SetElementText(_T(ELEMENT_INSTALL_STATUS), CComBSTR("")); | |
| TrackEvent(action); | |
| } | |
| bool CEndlessUsbToolDlg::FileHashingCallback(__int64 currentSize, __int64 totalSize, LPVOID context) | |
| { | |
| // RADU: do param verification | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg *)context; | |
| DWORD dwWaitStatus = WaitForSingleObject((HANDLE)dlg->m_cancelOperationEvent, 0); | |
| if(dwWaitStatus == WAIT_OBJECT_0) { | |
| uprintf("FileHashingCallback: Cancel requested."); | |
| return false; | |
| } | |
| float current = (float)(currentSize * 100 / totalSize); | |
| UpdateProgress(OP_VERIFYING_SIGNATURE, current); | |
| return true; | |
| } | |
| DWORD WINAPI CEndlessUsbToolDlg::FileVerificationThread(void* param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*) param; | |
| CString filename = dlg->m_localFile; | |
| CString signatureFilename = dlg->m_localFileSig; | |
| BOOL verificationResult = FALSE; | |
| signature_packet_t p_sig = { 0 }; | |
| public_key_t p_pkey = { 0 }; | |
| HCRYPTPROV hCryptProv = NULL; | |
| HCRYPTHASH hHash = NULL; | |
| uprintf("Verifying file '%ls' with signature '%ls'", dlg->m_localFile, dlg->m_localFileSig); | |
| /*verificationResult = TRUE; | |
| goto error;*/ | |
| IFFALSE_GOTOERROR(0 == LoadSignature(signatureFilename, &p_sig), "Error on LoadSignature"); | |
| IFFALSE_GOTOERROR(0 == parse_public_key(endless_public_key, sizeof(endless_public_key), &p_pkey, nullptr), "Error on parse_public_key"); | |
| IFFALSE_GOTOERROR(CryptAcquireContext(&hCryptProv, nullptr, nullptr, map_algo(p_pkey.key.algo), CRYPT_VERIFYCONTEXT), "Error on CryptAcquireContext"); | |
| memcpy(p_pkey.longid, endless_public_key_longid, sizeof(endless_public_key_longid)); | |
| IFFALSE_GOTOERROR(0 == memcmp(p_sig.issuer_longid, p_pkey.longid, 8), "Error: signature key id differs from Endless key id"); | |
| IFFALSE_GOTOERROR(CryptCreateHash(hCryptProv, map_digestalgo(p_sig.digest_algo), 0, 0, &hHash), "Error on CryptCreateHash"); | |
| IFFALSE_GOTOERROR(0 == hash_from_file(hHash, filename, &p_sig, FileHashingCallback, dlg), "Error on hash_from_file"); | |
| IFFALSE_GOTOERROR(0 == check_hash(hHash, &p_sig), "Error on check_hash"); | |
| IFFALSE_GOTOERROR(0 == verify_signature(hCryptProv, hHash, p_pkey, p_sig), "Error on verify_signature"); | |
| verificationResult = TRUE; | |
| error: | |
| ::PostMessage(hMainDialog, WM_FINISHED_FILE_VERIFICATION, (WPARAM)verificationResult, 0); | |
| // cleanup wincrypto | |
| if(hHash != NULL) CryptDestroyHash(hHash); | |
| if(hCryptProv != NULL) CryptReleaseContext(hCryptProv, 0); | |
| // cleanup key | |
| free(p_pkey.psz_username); | |
| // cleanup signature | |
| if (p_sig.version == 4) | |
| { | |
| free(p_sig.specific.v4.hashed_data); | |
| free(p_sig.specific.v4.unhashed_data); | |
| } | |
| return 0; | |
| } | |
| #if !defined(PARTITION_BASIC_DATA_GUID) | |
| const GUID PARTITION_BASIC_DATA_GUID = | |
| { 0xebd0a0a2L, 0xb9e5, 0x4433,{ 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } }; | |
| #endif | |
| #if !defined(PARTITION_SYSTEM_GUID) | |
| const GUID PARTITION_SYSTEM_GUID = | |
| { 0xc12a7328L, 0xf81f, 0x11d2,{ 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }; | |
| #endif | |
| #if !defined(PARTITION_BIOS_BOOT_GUID) | |
| const GUID PARTITION_BIOS_BOOT_GUID = | |
| { 0x21686148L, 0x6449, 0x6e6f, { 0x74, 0x4e, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } }; | |
| #endif | |
| #define EXPECTED_NUMBER_OF_PARTITIONS 3 | |
| #define EXFAT_PARTITION_NAME_IMAGES L"eosimages" | |
| #define EXFAT_PARTITION_NAME_LIVE L"eoslive" | |
| DWORD WINAPI CEndlessUsbToolDlg::FileCopyThread(void* param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| HANDLE hPhysical = INVALID_HANDLE_VALUE; | |
| DWORD size; | |
| DWORD DriveIndex = SelectedDrive.DeviceNumber; | |
| BOOL result; | |
| BYTE geometry[256] = { 0 }, layout[4096] = { 0 }; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | |
| CString driveDestination, fileDestination, liveFileName; | |
| CStringA iniLanguage = INI_LOCALE_EN; | |
| // Radu: why do we do this? | |
| memset(&SelectedDrive, 0, sizeof(SelectedDrive)); | |
| // Query for disk and partition data | |
| hPhysical = GetPhysicalHandle(DriveIndex, TRUE, TRUE); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk geometry."); | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk layout."); | |
| IFFALSE_GOTOERROR(DriveLayout->PartitionStyle == PARTITION_STYLE_GPT, "Unexpected partition type."); | |
| IFFALSE_GOTOERROR(DriveLayout->PartitionCount == EXPECTED_NUMBER_OF_PARTITIONS, "Error: Unexpected number of partitions."); | |
| uprintf("Partition type: GPT, NB Partitions: %d\n", DriveLayout->PartitionCount); | |
| uprintf("Disk GUID: %s\n", GuidToString(&DriveLayout->Gpt.DiskId)); | |
| uprintf("Max parts: %d, Start Offset: %I64i, Usable = %I64i bytes\n", | |
| DriveLayout->Gpt.MaxPartitionCount, DriveLayout->Gpt.StartingUsableOffset.QuadPart, DriveLayout->Gpt.UsableLength.QuadPart); | |
| // Move all partitions by one so we can add the exfat to the beginning of the partition table | |
| for (int index = EXPECTED_NUMBER_OF_PARTITIONS; index > 0 ; index--) { | |
| memcpy(&(DriveLayout->PartitionEntry[index]), &(DriveLayout->PartitionEntry[index - 1]), sizeof(PARTITION_INFORMATION_EX)); | |
| DriveLayout->PartitionEntry[index].PartitionNumber = index + 1; | |
| } | |
| // Create the partition to occupy available disk space | |
| PARTITION_INFORMATION_EX *lastPartition = &(DriveLayout->PartitionEntry[DriveLayout->PartitionCount]); | |
| PARTITION_INFORMATION_EX *newPartition = &(DriveLayout->PartitionEntry[0]); | |
| newPartition->PartitionStyle = PARTITION_STYLE_GPT; | |
| newPartition->PartitionNumber = 1;; | |
| newPartition->StartingOffset.QuadPart = lastPartition->StartingOffset.QuadPart + lastPartition->PartitionLength.QuadPart; | |
| newPartition->PartitionLength.QuadPart = DriveLayout->Gpt.UsableLength.QuadPart - newPartition->StartingOffset.QuadPart; | |
| newPartition->Gpt.PartitionType = PARTITION_BASIC_DATA_GUID; | |
| IGNORE_RETVAL(CoCreateGuid(&newPartition->Gpt.PartitionId)); | |
| wcscpy(newPartition->Gpt.Name, EXFAT_PARTITION_NAME_IMAGES); | |
| DriveLayout->PartitionCount += 1; | |
| size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX); | |
| IFFALSE_GOTOERROR(DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, layout, size, NULL, 0, &size, NULL), "Could not set drive layout."); | |
| result = RefreshDriveLayout(hPhysical); | |
| safe_closehandle(hPhysical); | |
| // Check if user cancelled | |
| IFFALSE_GOTOERROR(WaitForSingleObject(dlg->m_cancelOperationEvent, 0) == WAIT_TIMEOUT, "User cancel."); | |
| // Format the partition | |
| IFFALSE_GOTOERROR(FormatFirstPartitionOnDrive(DriveIndex, FS_EXFAT, dlg->m_cancelOperationEvent, EXFAT_PARTITION_NAME_IMAGES), "Error on FormatFirstPartitionOnDrive"); | |
| // Mount it | |
| IFFALSE_GOTOERROR(MountFirstPartitionOnDrive(DriveIndex, driveDestination), "Error on MountFirstPartitionOnDrive"); | |
| // Copy Files | |
| liveFileName = CSTRING_GET_LAST(dlg->m_LiveFile, L'\\'); | |
| if (liveFileName == ENDLESS_IMG_FILE_NAME) { | |
| fileDestination = driveDestination + CSTRING_GET_PATH(CSTRING_GET_LAST(dlg->m_LiveFileSig, L'\\'), L'.'); | |
| } else { | |
| fileDestination = driveDestination + liveFileName; | |
| } | |
| result = CopyFileEx(dlg->m_LiveFile, fileDestination, CEndlessUsbToolDlg::CopyProgressRoutine, dlg, NULL, 0); | |
| IFFALSE_GOTOERROR(result, "Copying live image failed/cancelled."); | |
| fileDestination = driveDestination + CSTRING_GET_LAST(dlg->m_LiveFileSig, L'\\'); | |
| result = CopyFileEx(dlg->m_LiveFileSig, fileDestination, NULL, NULL, NULL, 0); | |
| IFFALSE_GOTOERROR(result, "Copying live image signature failed."); | |
| // Create settings file | |
| if (!m_localeToIniLocale.Lookup(dlg->m_selectedLocale->txt[0], iniLanguage)) { | |
| uprintf("ERROR: Selected language not found %s. Defaulting to English", dlg->m_selectedLocale->txt[0]); | |
| } | |
| fileDestination = driveDestination + L"install.ini"; | |
| FILE *iniFile; | |
| if (0 == _wfopen_s(&iniFile, fileDestination, L"w")) { | |
| CStringA line = "[EndlessOS]\n"; | |
| fwrite((const char*)line, 1, line.GetLength(), iniFile); | |
| line.Format("locale=%s\n", iniLanguage); | |
| fwrite((const char*)line, 1, line.GetLength(), iniFile); | |
| fclose(iniFile); | |
| } else { | |
| uprintf("Could not open settings file %ls", fileDestination); | |
| } | |
| // Unmount | |
| if (!DeleteVolumeMountPoint(driveDestination)) { | |
| uprintf("Failed to unmount volume: %s", WindowsErrorString()); | |
| } | |
| FormatStatus = 0; | |
| safe_closehandle(hPhysical); | |
| dlg->PostMessage(WM_FINISHED_FILE_COPY, 0, 0); | |
| return 0; | |
| error: | |
| safe_closehandle(hPhysical); | |
| uprintf("FileCopyThread exited with error."); | |
| if (dlg->m_lastErrorCause == ErrorCause_t::ErrorCauseNone) { | |
| dlg->m_lastErrorCause = ErrorCause_t::ErrorCauseWriteFailed; | |
| } | |
| dlg->PostMessage(WM_FINISHED_FILE_COPY, 0, 0); | |
| return 0; | |
| } | |
| DWORD CALLBACK CEndlessUsbToolDlg::CopyProgressRoutine( | |
| LARGE_INTEGER TotalFileSize, | |
| LARGE_INTEGER TotalBytesTransferred, | |
| LARGE_INTEGER StreamSize, | |
| LARGE_INTEGER StreamBytesTransferred, | |
| DWORD dwStreamNumber, | |
| DWORD dwCallbackReason, | |
| HANDLE hSourceFile, | |
| HANDLE hDestinationFile, | |
| LPVOID lpData | |
| ) | |
| { | |
| //FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)lpData; | |
| DWORD dwWaitStatus = WaitForSingleObject((HANDLE)dlg->m_cancelOperationEvent, 0); | |
| if (dwWaitStatus == WAIT_OBJECT_0) { | |
| uprintf("CopyProgressRoutine: Cancel requested."); | |
| return PROGRESS_CANCEL; | |
| } | |
| if (dlg->m_currentStep == OP_SETUP_DUALBOOT || dlg->m_currentStep ==OP_NEW_LIVE_CREATION) { | |
| CEndlessUsbToolDlg::ImageUnpackCallback(TotalBytesTransferred.QuadPart); | |
| } else { | |
| float current = (float)(TotalBytesTransferred.QuadPart * 100 / TotalFileSize.QuadPart); | |
| UpdateProgress(OP_FILE_COPY, current); | |
| } | |
| return PROGRESS_CONTINUE; | |
| } | |
| bool CEndlessUsbToolDlg::ParseImgFileName(const CString& filename, CString &personality, CString &version, CString &date, bool &installerImage) | |
| { | |
| FUNCTION_ENTER; | |
| // parse filename to get personality and version | |
| CString lastPart; | |
| PCTSTR t1 = _T("-"), t2 = _T("."); | |
| int pos = 0; | |
| // RADU: Add some more validation here for the filename | |
| CString resToken = filename.Tokenize(t1, pos); | |
| CString product; | |
| int elemIndex = 0; | |
| while (!resToken.IsEmpty()) { | |
| switch (elemIndex) { | |
| case 0: product = resToken; break; | |
| case 1: version = resToken; break; | |
| case 3: date = resToken; break; | |
| case 4: lastPart = resToken; break; | |
| } | |
| resToken = filename.Tokenize(t1, pos); | |
| elemIndex++; | |
| }; | |
| version.Replace(_T(EOS_PRODUCT_TEXT), _T("")); | |
| IFFALSE_GOTOERROR(!version.IsEmpty() && !lastPart.IsEmpty(), ""); | |
| installerImage = product == _T(EOS_INSTALLER_PRODUCT_TEXT); | |
| if (product != _T(EOS_PRODUCT_TEXT) && | |
| product != _T(EOS_NONFREE_PRODUCT_TEXT) && | |
| product != _T(EOS_OEM_PRODUCT_TEXT) && | |
| product != _T(EOS_RETAIL_PRODUCT_TEXT) && | |
| !installerImage) { | |
| uprintf("Unrecognised product name '%ls'; assuming it's some new product", product); | |
| } | |
| date = date.Right(date.GetLength() - date.Find('.') - 1); | |
| pos = 0; | |
| resToken = lastPart.Tokenize(t2, pos); | |
| elemIndex = 0; | |
| while (!resToken.IsEmpty()) { | |
| switch (elemIndex) { | |
| case 1: personality = resToken; break; | |
| case 4: goto error; break; // we also have diskX | |
| } | |
| resToken = lastPart.Tokenize(t2, pos); | |
| elemIndex++; | |
| }; | |
| uint32_t msgId; | |
| IFFALSE_GOTOERROR(!personality.IsEmpty() && m_personalityToLocaleMsg.Lookup(personality, msgId), ""); | |
| return true; | |
| error: | |
| return false; | |
| } | |
| void CEndlessUsbToolDlg::GetImgDisplayName(CString &displayName, const CString &version, const CString &personality, ULONGLONG size) | |
| { | |
| FUNCTION_ENTER; | |
| // TODO: the JSON file is only parsed once, so m_selectedInstallMethod might well be a different value to what the user ultimately selects. | |
| // We should format the image size at the last possible moment. | |
| ULONGLONG actualsize = m_selectedInstallMethod == InstallMethod_t::ReformatterUsb ? (size + m_installerImage.compressedSize) : size; | |
| // Create display name | |
| displayName = _T(ENDLESS_OS); | |
| displayName += " "; | |
| displayName += version; | |
| displayName += " "; | |
| displayName += UTF8ToCString(lmprintf(m_personalityToLocaleMsg[personality])); | |
| if (personality != PERSONALITY_BASE) { | |
| displayName += " "; | |
| displayName += UTF8ToCString(lmprintf(MSG_316)); | |
| } | |
| if (size != 0) { | |
| displayName += " - "; | |
| displayName += SizeToHumanReadable(actualsize, FALSE, use_fake_units); | |
| } | |
| } | |
| int CEndlessUsbToolDlg::GetCompressionType(const CString& filename) | |
| { | |
| FUNCTION_ENTER; | |
| CString ext = CSTRING_GET_LAST(filename, '.'); | |
| if (ext == "gz") return BLED_COMPRESSION_GZIP; | |
| if (ext == "xz") return BLED_COMPRESSION_XZ; | |
| uprintf("%ls has unknown compression type %ls", filename, ext); | |
| return 0; | |
| } | |
| ULONGLONG CEndlessUsbToolDlg::GetExtractedSize(const CString& filename, BOOL isInstallerImage) | |
| { | |
| FUNCTION_ENTER; | |
| int compression_type = GetCompressionType(filename); | |
| IFFALSE_RETURN_VALUE(compression_type > BLED_COMPRESSION_NONE && compression_type < BLED_COMPRESSION_MAX, "unknown compression", 0); | |
| CStringA asciiFileName = ConvertUnicodeToUTF8(filename); | |
| return get_eos_archive_disk_image_size(asciiFileName, compression_type, isInstallerImage); | |
| } | |
| void CEndlessUsbToolDlg::GetIEVersion() | |
| { | |
| FUNCTION_ENTER; | |
| CRegKey registryKey; | |
| LSTATUS result; | |
| wchar_t versionValue[256]; | |
| ULONG size = _countof(versionValue); | |
| result = registryKey.Open(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Internet Explorer", KEY_QUERY_VALUE); | |
| IFFALSE_RETURN(result == ERROR_SUCCESS, "Error opening IE registry key."); | |
| // Quoth <https://support.microsoft.com/en-us/kb/969393>: | |
| // "The version string value for Internet Explorer 10 is 9.10.9200.16384, | |
| // and the svcVersion string value is 10.0.9200.16384." | |
| result = registryKey.QueryStringValue(L"svcVersion", versionValue, &size); | |
| if (result != ERROR_SUCCESS) { | |
| size = _countof(versionValue); | |
| result = registryKey.QueryStringValue(L"Version", versionValue, &size); | |
| } | |
| IFFALSE_RETURN(result == ERROR_SUCCESS, "Error Querying for version value."); | |
| uprintf("Internet Explorer %ls", versionValue); | |
| CString version = versionValue; | |
| version = version.Left(version.Find(L'.')); | |
| m_ieVersion = _wtoi(version); | |
| } | |
| DWORD WINAPI CEndlessUsbToolDlg::UpdateDownloadProgressThread(void* param) | |
| { | |
| FUNCTION_ENTER; | |
| CComPtr<IBackgroundCopyManager> bcManager; | |
| CComPtr<IBackgroundCopyJob> currentJob; | |
| DownloadStatus_t *downloadStatus = NULL; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| RemoteImageEntry_t remote; | |
| DownloadType_t downloadType = dlg->GetSelectedDownloadType(); | |
| POSITION pos = dlg->m_remoteImages.FindIndex(dlg->m_selectedRemoteIndex); | |
| CString jobName; | |
| IFFALSE_RETURN_VALUE(pos != NULL, "Index value not valid.", 0); | |
| remote = dlg->m_remoteImages.GetAt(pos); | |
| // Specify the appropriate COM threading model for your application. | |
| HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); | |
| IFFALSE_GOTO(SUCCEEDED(hr), "Error on calling CoInitializeEx", done); | |
| hr = CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyManager), (void**)&bcManager); | |
| IFFALSE_GOTO(SUCCEEDED(hr), "Error creating instance of BackgroundCopyManager.", done); | |
| jobName = DownloadManager::GetJobName(downloadType); | |
| jobName += remote.downloadJobName; | |
| IFFALSE_GOTO(SUCCEEDED(DownloadManager::GetExistingJob(bcManager, jobName, currentJob)), "No download job found", done); | |
| while (TRUE) | |
| { | |
| DWORD dwStatus = WaitForSingleObject(dlg->m_cancelOperationEvent, UPDATE_DOWNLOAD_PROGRESS_TIME); | |
| switch (dwStatus) { | |
| case WAIT_OBJECT_0: | |
| { | |
| uprintf("CEndlessUsbToolDlg::UpdateDownloadProgressThread cancel requested"); | |
| goto done; | |
| break; | |
| } | |
| case WAIT_TIMEOUT: | |
| { | |
| DownloadStatus_t downloadStatus; | |
| bool result = dlg->m_downloadManager.GetDownloadProgress(currentJob, downloadStatus, jobName); | |
| if (!result || downloadStatus.done || downloadStatus.error) { | |
| uprintf("CEndlessUsbToolDlg::UpdateDownloadProgressThread - Exiting"); | |
| goto done; | |
| } | |
| ::PostMessage(dlg->m_hWnd, WM_FILE_DOWNLOAD_STATUS, (WPARAM) new DownloadStatus_t(downloadStatus), 0); | |
| break; | |
| } | |
| } | |
| } | |
| done: | |
| dlg->m_downloadUpdateThread = INVALID_HANDLE_VALUE; | |
| CoUninitialize(); | |
| return 0; | |
| } | |
| DWORD WINAPI CEndlessUsbToolDlg::CheckInternetConnectionThread(void* param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| DWORD flags; | |
| BOOL result = FALSE, connected = FALSE, firstTime = TRUE; | |
| while (TRUE) { | |
| DWORD dwStatus = WaitForSingleObject(dlg->m_cancelOperationEvent, firstTime ? 0 : CHECK_INTERNET_CONNECTION_TIME); | |
| switch (dwStatus) { | |
| case WAIT_OBJECT_0: | |
| { | |
| uprintf("CEndlessUsbToolDlg::CheckInternetConnectionThread cancel requested"); | |
| goto done; | |
| } | |
| case WAIT_TIMEOUT: | |
| { | |
| flags = 0; | |
| result = InternetGetConnectedState(&flags, 0); | |
| break; | |
| } | |
| } | |
| if (firstTime || result != connected) { | |
| uprintf("InternetGetConnectedState changed: %s (flags %d)", result ? "online" : "offline", flags); | |
| firstTime = FALSE; | |
| connected = result; | |
| ::PostMessage(dlg->m_hWnd, WM_INTERNET_CONNECTION_STATE, (WPARAM)connected, 0); | |
| } | |
| } | |
| done: | |
| dlg->m_checkConnectionThread = INVALID_HANDLE_VALUE; | |
| return 0; | |
| } | |
| void CEndlessUsbToolDlg::EnableHibernate(bool enable) | |
| { | |
| FUNCTION_ENTER; | |
| EXECUTION_STATE flags; | |
| if (!enable) { | |
| if (nWindowsVersion == WINDOWS_XP) { | |
| flags = ES_CONTINUOUS | ES_SYSTEM_REQUIRED; | |
| } else { | |
| flags = ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED; | |
| } | |
| } else { | |
| flags = ES_CONTINUOUS; | |
| } | |
| SetThreadExecutionState(flags); | |
| } | |
| void CEndlessUsbToolDlg::CancelRunningOperation(bool userCancel) | |
| { | |
| FUNCTION_ENTER; | |
| SetEvent(m_cancelOperationEvent); | |
| m_cancelImageUnpack = 1; | |
| FormatStatus = FORMAT_STATUS_CANCEL; | |
| if (m_currentStep != OP_FLASHING_DEVICE) { | |
| m_downloadManager.ClearExtraDownloadJobs(userCancel); | |
| PostMessage(WM_FINISHED_ALL_OPERATIONS, (WPARAM)FALSE, 0); | |
| } | |
| } | |
| void CEndlessUsbToolDlg::StartCheckInternetConnectionThread() | |
| { | |
| if (m_checkConnectionThread == INVALID_HANDLE_VALUE) { | |
| m_checkConnectionThread = CreateThread(NULL, 0, CEndlessUsbToolDlg::CheckInternetConnectionThread, (LPVOID)this, 0, NULL); | |
| } | |
| } | |
| bool CEndlessUsbToolDlg::CanUseLocalFile() | |
| { | |
| //RADU: check if installer version matches local images version and display only the images that match? | |
| bool hasLocalInstaller = m_selectedInstallMethod == InstallMethod_t::LiveUsb || (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb && m_localInstallerImage.stillPresent == TRUE); | |
| bool hasLocalImages = (m_imageFiles.GetCount() != 0); | |
| // If we have a local entry with all needed files | |
| bool hasFilesForDualBoot = false; | |
| if (IsDualBootOrCombinedUsb() && hasLocalImages) { | |
| pFileImageEntry_t currentEntry = NULL; | |
| CString path; | |
| for (POSITION position = m_imageFiles.GetStartPosition(); position != NULL; ) { | |
| m_imageFiles.GetNextAssoc(position, path, currentEntry); | |
| bool imageBootSupport = HasImageBootSupport(currentEntry->version, currentEntry->date); | |
| if (imageBootSupport && currentEntry->hasBootArchive && currentEntry->hasBootArchiveSig &¤tEntry->hasUnpackedImgSig) { | |
| hasFilesForDualBoot = true; | |
| break; | |
| } | |
| } | |
| } | |
| return !m_localFilesScanned || (hasLocalInstaller && hasLocalImages) || hasFilesForDualBoot; | |
| } | |
| // Returns true if we might, in principle, be able to use a remote file. | |
| bool CEndlessUsbToolDlg::CanUseRemoteFile() | |
| { | |
| return m_jsonDownloadState != JSONDownloadState::Failed || m_remoteImages.GetCount() != 0; | |
| } | |
| void CEndlessUsbToolDlg::FindMaxUSBSpeed() | |
| { | |
| SP_DEVICE_INTERFACE_DATA DevIntfData; | |
| PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData; | |
| SP_DEVINFO_DATA DevData; | |
| HDEVINFO hDevInfo; | |
| DWORD dwMemberIdx, dwSize; | |
| m_maximumUSBVersion = USB_SPEED_UNKNOWN; | |
| hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_HUB, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); | |
| IFFALSE_RETURN(hDevInfo != INVALID_HANDLE_VALUE, "Error on SetupDiGetClassDevs call"); | |
| DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | |
| dwMemberIdx = 0; | |
| SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_HUB, dwMemberIdx, &DevIntfData); | |
| while (GetLastError() != ERROR_NO_MORE_ITEMS) { | |
| DevData.cbSize = sizeof(DevData); | |
| SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL); | |
| DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize); | |
| DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | |
| if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData, DevIntfDetailData, dwSize, &dwSize, &DevData)) { | |
| CheckUSBHub(DevIntfDetailData->DevicePath); | |
| } | |
| HeapFree(GetProcessHeap(), 0, DevIntfDetailData); | |
| SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_HUB, ++dwMemberIdx, &DevIntfData); | |
| } | |
| SetupDiDestroyDeviceInfoList(hDevInfo); | |
| } | |
| void CEndlessUsbToolDlg::CheckUSBHub(LPCTSTR devicePath) | |
| { | |
| HANDLE usbHubHandle = CreateFile(devicePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); | |
| if (usbHubHandle != INVALID_HANDLE_VALUE) { | |
| USB_HUB_CAPABILITIES hubCapabilities; | |
| DWORD size = sizeof(hubCapabilities); | |
| memset(&hubCapabilities, 0, sizeof(USB_HUB_CAPABILITIES)); | |
| // Windows XP check | |
| if (nWindowsVersion == WINDOWS_XP) { | |
| if (!DeviceIoControl(usbHubHandle, IOCTL_USB_GET_HUB_CAPABILITIES, &hubCapabilities, size, &hubCapabilities, size, &size, NULL)) { | |
| uprintf("Could not get hub capabilites for %ls: %s", devicePath, WindowsErrorString()); | |
| } else { | |
| uprintf("%ls HubIs2xCapable=%d", devicePath, hubCapabilities.HubIs2xCapable); | |
| m_maximumUSBVersion = max(m_maximumUSBVersion, hubCapabilities.HubIs2xCapable ? USB_SPEED_HIGH : USB_SPEED_LOW); | |
| } | |
| } | |
| // Windows Vista and 7 check | |
| if (nWindowsVersion >= WINDOWS_VISTA) { | |
| USB_HUB_CAPABILITIES_EX hubCapabilitiesEx; | |
| size = sizeof(hubCapabilitiesEx); | |
| memset(&hubCapabilitiesEx, 0, sizeof(USB_HUB_CAPABILITIES_EX)); | |
| if (!DeviceIoControl(usbHubHandle, IOCTL_USB_GET_HUB_CAPABILITIES_EX, &hubCapabilitiesEx, size, &hubCapabilitiesEx, size, &size, NULL)) { | |
| uprintf("Could not get hub capabilites for %ls: %s", devicePath, WindowsErrorString()); | |
| } else { | |
| uprintf("Vista+ %ls HubIsHighSpeedCapable=%d, HubIsHighSpeed=%d", | |
| devicePath, hubCapabilitiesEx.CapabilityFlags.HubIsHighSpeedCapable, hubCapabilitiesEx.CapabilityFlags.HubIsHighSpeed); | |
| m_maximumUSBVersion = max(m_maximumUSBVersion, hubCapabilitiesEx.CapabilityFlags.HubIsHighSpeed ? USB_SPEED_HIGH : USB_SPEED_LOW); | |
| } | |
| } | |
| // Windows 8 and later check | |
| if (nWindowsVersion >= WINDOWS_8) { | |
| USB_HUB_INFORMATION_EX hubInformation; | |
| size = sizeof(hubInformation); | |
| memset(&hubInformation, 0, sizeof(USB_HUB_INFORMATION_EX)); | |
| if (!DeviceIoControl(usbHubHandle, IOCTL_USB_GET_HUB_INFORMATION_EX, &hubInformation, size, &hubInformation, size, &size, NULL)) { | |
| uprintf("Could not get hub capabilites for %ls: %s", devicePath, WindowsErrorString()); | |
| } else { | |
| USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2; | |
| for (int index = 1; index <= hubInformation.HighestPortNumber; index++) { | |
| memset(&conn_info_v2, 0, sizeof(conn_info_v2)); | |
| size = sizeof(conn_info_v2); | |
| conn_info_v2.ConnectionIndex = (ULONG)index; | |
| conn_info_v2.Length = size; | |
| conn_info_v2.SupportedUsbProtocols.Usb300 = 1; | |
| if (!DeviceIoControl(usbHubHandle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, &conn_info_v2, size, &conn_info_v2, size, &size, NULL)) { | |
| uprintf("Could not get node connection information (V2) for hub '%ls' with port '%d': %s", devicePath, index, WindowsErrorString()); | |
| continue; | |
| } | |
| uprintf("%d) USB 3.0(%d), 2.0(%d), 1.0(%d), OperatingAtSuperSpeedOrHigher (%d), SuperSpeedCapableOrHigher (%d)", index, | |
| conn_info_v2.SupportedUsbProtocols.Usb300, conn_info_v2.SupportedUsbProtocols.Usb200, conn_info_v2.SupportedUsbProtocols.Usb110, | |
| conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher, conn_info_v2.Flags.DeviceIsSuperSpeedCapableOrHigher); | |
| m_maximumUSBVersion = max(m_maximumUSBVersion, conn_info_v2.SupportedUsbProtocols.Usb300 == 1 ? USB_SPEED_SUPER_OR_LATER : USB_SPEED_HIGH); | |
| } | |
| } | |
| } | |
| } else { | |
| uprintf("Could not open hub %ls: %s", devicePath, WindowsErrorString()); | |
| } | |
| safe_closehandle(usbHubHandle); | |
| } | |
| void CEndlessUsbToolDlg::UpdateUSBSpeedMessage(int deviceIndex) | |
| { | |
| if (deviceIndex < 0) { | |
| SetElementText(_T(ELEMENT_SELUSB_SPEEDWARNING), UTF8ToBSTR(lmprintf(MSG_372))); | |
| return; | |
| } | |
| DWORD speed = usbDeviceSpeed[deviceIndex]; | |
| BOOL isLowerSpeed = usbDeviceSpeedIsLower[deviceIndex]; | |
| DWORD msgId = 0; | |
| if (speed < USB_SPEED_HIGH) { // smaller than USB 2.0 | |
| if (m_maximumUSBVersion == USB_SPEED_SUPER_OR_LATER) { // we have USB 3.0 ports | |
| msgId = MSG_330; | |
| } else { // we have USB 2.0 ports | |
| msgId = MSG_331; | |
| } | |
| } else if (speed == USB_SPEED_HIGH && m_maximumUSBVersion == USB_SPEED_SUPER_OR_LATER) { // USB 2.0 device (or USB 3.0 device in USB 2.0 port) and we have USB 3.0 ports | |
| msgId = isLowerSpeed ? MSG_333 : MSG_332; | |
| } | |
| if (msgId == 0) { | |
| SetElementText(_T(ELEMENT_SELUSB_SPEEDWARNING), CComBSTR("")); | |
| } else { | |
| SetElementText(_T(ELEMENT_SELUSB_SPEEDWARNING), UTF8ToBSTR(lmprintf(msgId))); | |
| } | |
| } | |
| void CEndlessUsbToolDlg::SetJSONDownloadState(JSONDownloadState state) | |
| { | |
| FUNCTION_ENTER_FMT("%d", state); | |
| m_jsonDownloadState = state; | |
| if (state == JSONDownloadState::Failed) { | |
| TrackEvent(_T("Failed"), _T("ErrorCauseJSONDownloadFailed")); | |
| } | |
| if (state == JSONDownloadState::Succeeded) { | |
| // TODO: dissociate this event from the install method | |
| TrackEvent(_T("JSONDownloadSucceeded")); | |
| } | |
| UpdateDownloadableState(); | |
| } | |
| #define GPT_MAX_PARTITION_COUNT 128 | |
| #define ESP_PART_STARTING_SECTOR 2048 | |
| #define ESP_PART_LENGTH_BYTES 65011712 | |
| #define MBR_PART_STARTING_SECTOR 129024 | |
| #define MBR_PART_LENGTH_BYTES 1048576 | |
| #define EXFAT_PART_STARTING_SECTOR 131072 | |
| #define EFI_BOOT_SUBDIRECTORY L"EFI\\BOOT" | |
| #define ENDLESS_BOOT_SUBDIRECTORY L"EFI\\Endless" | |
| #define GRUB_BOOT_SUBDIRECTORY L"grub" | |
| #define LIVE_BOOT_IMG_FILE L"live\\boot.img" | |
| #define LIVE_CORE_IMG_FILE L"live\\core.img" | |
| /* The offset of a magic number used by Windows NT. */ | |
| #define MBR_WINDOWS_NT_MAGIC 0x1b8 | |
| /* The offset of the start of the partition table. */ | |
| #define MBR_PART_START 0x1be | |
| /* The size of boot.img produced by the Endless OS image builder */ | |
| #define MAX_BOOT_IMG_FILE_SIZE MBR_PART_START | |
| #define NTFS_BOOT_IMG_FILE L"ntfs\\boot.img" | |
| #define NTFS_CORE_IMG_FILE L"ntfs\\core.img" | |
| #define ENDLESS_BOOT_EFI_FILE "bootx64.efi" | |
| #define BACKUP_BOOTTRACK_IMG L"boottrack.img" | |
| #define BACKUP_MBR_IMG L"mbr.img" | |
| #define MAX_BOOTTRACK_SIZE (4 * 1024 * 1024) // 4Mb | |
| /* The offset of BOOT_DRIVE_CHECK. */ | |
| #define GRUB_BOOT_MACHINE_DRIVE_CHECK 0x66 | |
| // Below defines need to be in this order | |
| #define USB_PROGRESS_UNPACK_BOOT_ZIP 2 | |
| #define USB_PROGRESS_MBR_SBR_DONE 4 | |
| #define USB_PROGRESS_ESP_CREATION_DONE 6 | |
| #define USB_PROGRESS_EXFAT_PREPARED 10 | |
| #define USB_PROGRESS_IMG_COPY_DONE 98 | |
| #define USB_PROGRESS_ALL_DONE 100 | |
| #define CHECK_IF_CANCELLED IFFALSE_GOTOERROR(dlg->m_cancelImageUnpack == 0 && WaitForSingleObject((HANDLE)dlg->m_cancelOperationEvent, 0) != WAIT_OBJECT_0, "Operation has been cancelled") | |
| DWORD WINAPI CEndlessUsbToolDlg::CreateUSBStick(LPVOID param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| DWORD DriveIndex = SelectedDrive.DeviceNumber; | |
| BOOL result; | |
| DWORD size; | |
| HANDLE hPhysical = INVALID_HANDLE_VALUE; | |
| BYTE geometry[256] = { 0 }, layout[4096] = { 0 }; | |
| CREATE_DISK createDiskData; | |
| CString driveLetter; | |
| CString bootFilesPathGz = dlg->m_bootArchive; | |
| CString bootFilesPath = CEndlessUsbToolApp::TempFilePath(CString(BOOT_COMPONENTS_FOLDER)) + L"\\"; | |
| CString usbFilesPath; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| UpdateProgress(OP_NEW_LIVE_CREATION, 0); | |
| // Unpack boot components | |
| IFFALSE_GOTOERROR(UnpackBootComponents(bootFilesPathGz, bootFilesPath), "Error unpacking boot components."); | |
| UpdateProgress(OP_NEW_LIVE_CREATION, USB_PROGRESS_UNPACK_BOOT_ZIP); | |
| CHECK_IF_CANCELLED; | |
| // initialize create disk data | |
| memset(&createDiskData, 0, sizeof(createDiskData)); | |
| createDiskData.PartitionStyle = PARTITION_STYLE_GPT; | |
| createDiskData.Gpt.MaxPartitionCount = GPT_MAX_PARTITION_COUNT; | |
| // get disk handle | |
| hPhysical = GetPhysicalHandle(DriveIndex, TRUE, TRUE); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| // initialize the disk | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_CREATE_DISK, &createDiskData, sizeof(createDiskData), NULL, 0, &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0, "Error when calling IOCTL_DISK_CREATE_DISK"); | |
| // get disk geometry information | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk geometry."); | |
| // set initial drive layout data | |
| memset(layout, 0, sizeof(layout)); | |
| IFFALSE_GOTOERROR(CreateFakePartitionLayout(hPhysical, layout, geometry), "Error on CreateFakePartitionLayout"); | |
| CHECK_IF_CANCELLED; | |
| // Write MBR and SBR to disk | |
| IFFALSE_GOTOERROR(WriteMBRAndSBRToUSB(hPhysical, bootFilesPath, DiskGeometry->Geometry.BytesPerSector), "Error on WriteMBRAndSBRToUSB"); | |
| safe_closehandle(hPhysical); | |
| UpdateProgress(OP_NEW_LIVE_CREATION, USB_PROGRESS_MBR_SBR_DONE); | |
| CHECK_IF_CANCELLED; | |
| // Format and mount ESP | |
| IFFALSE_GOTOERROR(FormatFirstPartitionOnDrive(DriveIndex, FS_FAT32, dlg->m_cancelOperationEvent, L""), "Error on FormatFirstPartitionOnDrive"); | |
| CHECK_IF_CANCELLED; | |
| IFFALSE_GOTOERROR(MountFirstPartitionOnDrive(DriveIndex, driveLetter), "Error on MountFirstPartitionOnDrive"); | |
| // Copy files to the ESP partition | |
| IFFALSE_GOTOERROR(CopyFilesToESP(bootFilesPath, driveLetter), "Error when trying to copy files to ESP partition."); | |
| // Unmount ESP | |
| if (!DeleteVolumeMountPoint(driveLetter)) uprintf("Failed to unmount volume: %s", WindowsErrorString()); | |
| UpdateProgress(OP_NEW_LIVE_CREATION, USB_PROGRESS_ESP_CREATION_DONE); | |
| CHECK_IF_CANCELLED; | |
| // get disk handle again | |
| hPhysical = GetPhysicalHandle(DriveIndex, TRUE, TRUE); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| // fix partition types and layout | |
| IFFALSE_GOTOERROR(CreateCorrectPartitionLayout(hPhysical, layout, geometry), "Error on CreateCorrectPartitionLayout"); | |
| safe_closehandle(hPhysical); | |
| CHECK_IF_CANCELLED; | |
| // Format and mount exFAT | |
| IFFALSE_GOTOERROR(FormatFirstPartitionOnDrive(DriveIndex, FS_EXFAT, dlg->m_cancelOperationEvent, EXFAT_PARTITION_NAME_LIVE), "Error on FormatFirstPartitionOnDrive"); | |
| CHECK_IF_CANCELLED; | |
| IFFALSE_GOTOERROR(MountFirstPartitionOnDrive(DriveIndex, driveLetter), "Error on MountFirstPartitionOnDrive"); | |
| UpdateProgress(OP_NEW_LIVE_CREATION, USB_PROGRESS_EXFAT_PREPARED); | |
| // Copy files to the exFAT partition | |
| IFFALSE_GOTOERROR(CopyFilesToexFAT(dlg, bootFilesPath, driveLetter), "Error on CopyFilesToexFAT"); | |
| // Unmount exFAT | |
| if (!DeleteVolumeMountPoint(driveLetter)) uprintf("Failed to unmount volume: %s", WindowsErrorString()); | |
| UpdateProgress(OP_NEW_LIVE_CREATION, USB_PROGRESS_ALL_DONE); | |
| CHECK_IF_CANCELLED; | |
| goto done; | |
| error: | |
| uprintf("CreateUSBStick exited with error."); | |
| if (dlg->m_lastErrorCause == ErrorCause_t::ErrorCauseNone) { | |
| dlg->m_lastErrorCause = ErrorCause_t::ErrorCauseWriteFailed; | |
| } | |
| // Unmount exFAT | |
| if (!DeleteVolumeMountPoint(driveLetter)) uprintf("Failed to unmount volume: %s", WindowsErrorString()); | |
| done: | |
| RemoveNonEmptyDirectory(bootFilesPath); | |
| safe_closehandle(hPhysical); | |
| dlg->PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| return 0; | |
| } | |
| bool CEndlessUsbToolDlg::CreateFakePartitionLayout(HANDLE hPhysical, PBYTE layout, PBYTE geometry) | |
| { | |
| FUNCTION_ENTER; | |
| PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| PARTITION_INFORMATION_EX *currentPartition; | |
| // the EFI spec says the GPT will take the first and last two sectors of | |
| // the disk, plus however much is allocated for partition entries. there | |
| // must be at least 128 entries of at least 128 in size - in practice this | |
| // is what everyone uses. 128 * 128 is 16k which will always be aligned to | |
| // sector size which is at least 512, but a hypothetical GIANT SECTOR disk | |
| // would need three sectors reserved for the whole partition table, so we | |
| // use max() | |
| LONGLONG gptLength = (2 * DiskGeometry->Geometry.BytesPerSector) + | |
| max((128 * 128), DiskGeometry->Geometry.BytesPerSector); | |
| bool returnValue = false; | |
| DriveLayout->PartitionStyle = PARTITION_STYLE_GPT; | |
| DriveLayout->PartitionCount = EXPECTED_NUMBER_OF_PARTITIONS; | |
| IGNORE_RETVAL(CoCreateGuid(&DriveLayout->Gpt.DiskId)); | |
| LONGLONG partitionStart[EXPECTED_NUMBER_OF_PARTITIONS] = { | |
| ESP_PART_STARTING_SECTOR * DiskGeometry->Geometry.BytesPerSector, | |
| MBR_PART_STARTING_SECTOR * DiskGeometry->Geometry.BytesPerSector, | |
| EXFAT_PART_STARTING_SECTOR * DiskGeometry->Geometry.BytesPerSector | |
| }; | |
| LONGLONG partitionSize[EXPECTED_NUMBER_OF_PARTITIONS] = { | |
| ESP_PART_LENGTH_BYTES, | |
| MBR_PART_LENGTH_BYTES, | |
| // there is a 2nd copy of the GPT at the end of the disk so we | |
| // subtract the length here to avoid the operation failing | |
| DiskGeometry->DiskSize.QuadPart - gptLength - partitionStart[2] | |
| }; | |
| GUID partitionType[EXPECTED_NUMBER_OF_PARTITIONS] = { | |
| PARTITION_BASIC_DATA_GUID, // will become PARTITION_SYSTEM_GUID later | |
| PARTITION_BIOS_BOOT_GUID, | |
| PARTITION_BASIC_DATA_GUID | |
| }; | |
| for (int partIndex = 0; partIndex < EXPECTED_NUMBER_OF_PARTITIONS; partIndex++) { | |
| currentPartition = &(DriveLayout->PartitionEntry[partIndex]); | |
| currentPartition->PartitionStyle = PARTITION_STYLE_GPT; | |
| currentPartition->StartingOffset.QuadPart = partitionStart[partIndex]; | |
| currentPartition->PartitionLength.QuadPart = partitionSize[partIndex]; | |
| currentPartition->PartitionNumber = partIndex + 1; // partition numbers start from 1 | |
| currentPartition->RewritePartition = TRUE; | |
| currentPartition->Gpt.PartitionType = partitionType[partIndex]; | |
| IGNORE_RETVAL(CoCreateGuid(¤tPartition->Gpt.PartitionId)); | |
| if (partIndex == EXPECTED_NUMBER_OF_PARTITIONS - 1) wcscpy(currentPartition->Gpt.Name, EXFAT_PARTITION_NAME_LIVE); | |
| } | |
| // push partition information to drive | |
| DWORD size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX); | |
| IFFALSE_GOTOERROR(DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, layout, size, NULL, 0, &size, NULL), "Could not set drive layout."); | |
| DWORD result = RefreshDriveLayout(hPhysical); | |
| IFFALSE_PRINTERROR(result != 0, "Warning: failure on IOCTL_DISK_UPDATE_PROPERTIES"); // not returning here, maybe formatting will still work although IOCTL_DISK_UPDATE_PROPERTIES failed | |
| returnValue = true; | |
| error: | |
| return returnValue; | |
| } | |
| bool CEndlessUsbToolDlg::CreateCorrectPartitionLayout(HANDLE hPhysical, PBYTE layout, PBYTE geometry) | |
| { | |
| FUNCTION_ENTER; | |
| PARTITION_INFORMATION_EX exfatPartition; | |
| PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| bool returnValue = false; | |
| // save the exFAT partition data | |
| memcpy(&exfatPartition, &(DriveLayout->PartitionEntry[2]), sizeof(PARTITION_INFORMATION_EX)); | |
| exfatPartition.PartitionNumber = 1; | |
| // move ESP and MBR | |
| memcpy(&(DriveLayout->PartitionEntry[2]), &(DriveLayout->PartitionEntry[1]), sizeof(PARTITION_INFORMATION_EX)); | |
| DriveLayout->PartitionEntry[2].PartitionNumber = 3; | |
| memcpy(&(DriveLayout->PartitionEntry[1]), &(DriveLayout->PartitionEntry[0]), sizeof(PARTITION_INFORMATION_EX)); | |
| DriveLayout->PartitionEntry[1].PartitionNumber = 2; | |
| DriveLayout->PartitionEntry[1].Gpt.PartitionType = PARTITION_SYSTEM_GUID; | |
| // copy exFAT to first position | |
| memcpy(&(DriveLayout->PartitionEntry[0]), &exfatPartition, sizeof(PARTITION_INFORMATION_EX)); | |
| // push partition information to drive | |
| DWORD size = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + DriveLayout->PartitionCount * sizeof(PARTITION_INFORMATION_EX); | |
| IFFALSE_GOTOERROR(DeviceIoControl(hPhysical, IOCTL_DISK_SET_DRIVE_LAYOUT_EX, layout, size, NULL, 0, &size, NULL), "Could not set drive layout."); | |
| DWORD result = RefreshDriveLayout(hPhysical); | |
| IFFALSE_PRINTERROR(result != 0, "Warning: failure on IOCTL_DISK_UPDATE_PROPERTIES"); // not returning here, maybe formatting will still work although IOCTL_DISK_UPDATE_PROPERTIES failed | |
| returnValue = true; | |
| error: | |
| return returnValue; | |
| } | |
| bool CEndlessUsbToolDlg::FormatFirstPartitionOnDrive(DWORD DriveIndex, int fsToUse, HANDLE cancelEvent, const wchar_t *partLabel) | |
| { | |
| FUNCTION_ENTER; | |
| BOOL result; | |
| int formatRetries = 5; | |
| bool returnValue = false; | |
| // Wait for the logical drive we just created to appear | |
| uprintf("Waiting for logical drive to reappear...\n"); | |
| Sleep(200); // Radu: check if this is needed, that's what rufus does; I hate sync using sleep | |
| IFFALSE_PRINTERROR(WaitForLogical(DriveIndex), "Warning: Logical drive was not found!"); // We try to continue even if this fails, just in case | |
| while (formatRetries-- > 0 && !(result = FormatDrive(DriveIndex, fsToUse, partLabel))) { | |
| Sleep(200); // Radu: check if this is needed, that's what rufus does; I hate sync using sleep | |
| // Check if user cancelled | |
| IFFALSE_GOTOERROR(WaitForSingleObject(cancelEvent, 0) == WAIT_TIMEOUT, "User cancel."); | |
| } | |
| IFFALSE_GOTOERROR(result, "Format error."); | |
| Sleep(200); // Radu: check if this is needed, that's what rufus does; I hate sync using sleep | |
| IFFALSE_PRINTERROR(WaitForLogical(DriveIndex), "Warning: Logical drive was not found after format!"); | |
| returnValue = true; | |
| error: | |
| return returnValue; | |
| } | |
| bool CEndlessUsbToolDlg::MountFirstPartitionOnDrive(DWORD DriveIndex, CString &driveLetter) | |
| { | |
| FUNCTION_ENTER; | |
| char *guid_volume = NULL; | |
| bool returnValue = false; | |
| guid_volume = GetLogicalName(DriveIndex, TRUE, TRUE); | |
| IFFALSE_GOTOERROR(guid_volume != NULL, "Could not get GUID volume name\n"); | |
| uprintf("Found volume GUID %s\n", guid_volume); | |
| // Mount partition | |
| char drive_name[] = "?:\\"; | |
| drive_name[0] = GetUnusedDriveLetter(); | |
| IFFALSE_GOTOERROR(drive_name[0] != 0, "Could not find an unused drive letter"); | |
| IFFALSE_GOTOERROR(MountVolume(drive_name, guid_volume), "Could not mount volume."); | |
| driveLetter = drive_name; | |
| returnValue = true; | |
| error: | |
| safe_free(guid_volume); | |
| return returnValue; | |
| } | |
| bool CEndlessUsbToolDlg::UnpackZip(const CComBSTR source, const CComBSTR dest) | |
| { | |
| FUNCTION_ENTER; | |
| HRESULT hResult; | |
| CComPtr<IShellDispatch> pISD; | |
| CComPtr<Folder> pToFolder, pFromFolder; | |
| CComPtr<FolderItems> folderItems; | |
| bool returnValue = false; | |
| IFFALSE_GOTOERROR(SUCCEEDED(CoInitialize(NULL)), "Error on CoInitialize"); | |
| hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hResult), "Error on CoCreateInstance with IID_IShellDispatch"); | |
| IFFALSE_GOTOERROR(SUCCEEDED(pISD->NameSpace(CComVariant(dest), &pToFolder)), "Error on pISD->NameSpace for destination."); | |
| IFFALSE_GOTOERROR(SUCCEEDED(pISD->NameSpace(CComVariant(source), &pFromFolder)), "Error on pISD->NameSpace for source."); | |
| IFFALSE_GOTOERROR(SUCCEEDED(pFromFolder->Items(&folderItems)), "Error on pFromFolder->Items."); | |
| IFFALSE_GOTOERROR(SUCCEEDED(pToFolder->CopyHere(CComVariant(folderItems), CComVariant(FOF_NO_UI))), "Error on pToFolder->CopyHere"); | |
| returnValue = true; | |
| error: | |
| CoUninitialize(); | |
| return returnValue; | |
| } | |
| void CEndlessUsbToolDlg::RemoveNonEmptyDirectory(const CString directoryPath) | |
| { | |
| FUNCTION_ENTER; | |
| SHFILEOPSTRUCT fileOperation; | |
| wchar_t dir[MAX_PATH + 1]; | |
| memset(dir, 0, sizeof(dir)); | |
| // On Windows XP, SHFileOperation fails with code 0x402 ("an unknown error occurred") | |
| // if there is a trailing slash on the path. In practice we always pass a string with | |
| // a trailing slash to this function, but let's not rely on that and explicitly check: | |
| const CString withoutTrailingSlash = directoryPath.Right(1) == _T("\\") | |
| ? directoryPath.Left(directoryPath.GetLength() - 1) | |
| : directoryPath; | |
| wsprintf(dir, L"%ls", withoutTrailingSlash); | |
| fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR; | |
| fileOperation.pFrom = dir; | |
| fileOperation.pTo = NULL; | |
| fileOperation.hwnd = NULL; | |
| fileOperation.wFunc = FO_DELETE; | |
| int result = SHFileOperation(&fileOperation); | |
| uprintf("Removing directory '%ls' result=%d", directoryPath, result); | |
| } | |
| bool CEndlessUsbToolDlg::CopyFilesToESP(const CString &fromFolder, const CString &driveLetter) | |
| { | |
| FUNCTION_ENTER; | |
| CString espFolder = driveLetter + EFI_BOOT_SUBDIRECTORY; | |
| SHFILEOPSTRUCT fileOperation; | |
| wchar_t fromPath[MAX_PATH + 1], toPath[MAX_PATH + 1]; | |
| bool retResult = false; | |
| int createDirResult = SHCreateDirectoryExW(NULL, espFolder, NULL); | |
| IFFALSE_GOTOERROR(createDirResult == ERROR_SUCCESS || createDirResult == ERROR_FILE_EXISTS, "Error creating EFI directory in ESP partition."); | |
| memset(fromPath, 0, sizeof(fromPath)); | |
| wsprintf(fromPath, L"%ls%ls\\%ls", fromFolder, EFI_BOOT_SUBDIRECTORY, ALL_FILES); | |
| memset(toPath, 0, sizeof(fromPath)); | |
| wsprintf(toPath, L"%ls", espFolder); | |
| fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR; | |
| fileOperation.pFrom = fromPath; | |
| fileOperation.pTo = toPath; | |
| fileOperation.hwnd = NULL; | |
| fileOperation.wFunc = FO_COPY; | |
| int result = SHFileOperation(&fileOperation); | |
| retResult = result == ERROR_SUCCESS; | |
| error: | |
| return retResult; | |
| } | |
| void CEndlessUsbToolDlg::ImageUnpackCallback(const uint64_t read_bytes) | |
| { | |
| static int oldPercent = 0; | |
| static int oldOp = -1; | |
| float perecentageUnpacked = 100.0f * read_bytes / CEndlessUsbToolDlg::ImageUnpackFileSize; | |
| float percentage = (float)CEndlessUsbToolDlg::ImageUnpackPercentStart; | |
| percentage += (CEndlessUsbToolDlg::ImageUnpackPercentEnd - CEndlessUsbToolDlg::ImageUnpackPercentStart) * perecentageUnpacked / 100; | |
| bool change = false; | |
| if (CEndlessUsbToolDlg::ImageUnpackOperation != oldOp) { | |
| oldOp = CEndlessUsbToolDlg::ImageUnpackOperation; | |
| oldPercent = (int)floor(percentage); | |
| change = true; | |
| } else if (oldPercent != (int)floor(percentage)) { | |
| oldPercent = (int)floor(percentage); | |
| change = true; | |
| } | |
| if (change) { | |
| uprintf("Operation %ls(%d) - unpacked %s", | |
| OperationToStr(CEndlessUsbToolDlg::ImageUnpackOperation), | |
| CEndlessUsbToolDlg::ImageUnpackOperation, | |
| SizeToHumanReadable(read_bytes, FALSE, use_fake_units)); | |
| } | |
| UpdateProgress(CEndlessUsbToolDlg::ImageUnpackOperation, percentage); | |
| } | |
| bool CEndlessUsbToolDlg::CopyFilesToexFAT(CEndlessUsbToolDlg *dlg, const CString &fromFolder, const CString &driveLetter) | |
| { | |
| FUNCTION_ENTER; | |
| bool retResult = false; | |
| CString usbFilesPath = driveLetter + PATH_ENDLESS_SUBDIRECTORY; | |
| CString exePath = GetExePath(); | |
| CStringA originalImgFilename = ConvertUnicodeToUTF8(CSTRING_GET_PATH(CSTRING_GET_LAST(dlg->m_unpackedImageSig, '\\'), '.')); | |
| int createDirResult = SHCreateDirectoryExW(NULL, usbFilesPath, NULL); | |
| IFFALSE_GOTOERROR(createDirResult == ERROR_SUCCESS || createDirResult == ERROR_FILE_EXISTS, "Error creating directory on USB drive."); | |
| CEndlessUsbToolDlg::ImageUnpackOperation = OP_NEW_LIVE_CREATION; | |
| CEndlessUsbToolDlg::ImageUnpackPercentStart = USB_PROGRESS_EXFAT_PREPARED; | |
| CEndlessUsbToolDlg::ImageUnpackPercentEnd = USB_PROGRESS_IMG_COPY_DONE; | |
| CEndlessUsbToolDlg::ImageUnpackFileSize = dlg->m_selectedFileSize; | |
| if (CSTRING_GET_LAST(dlg->m_localFile, '\\') == ENDLESS_IMG_FILE_NAME) { | |
| BOOL result = CopyFileEx(dlg->m_localFile, usbFilesPath + ENDLESS_IMG_FILE_NAME, CEndlessUsbToolDlg::CopyProgressRoutine, dlg, NULL, 0); | |
| IFFALSE_GOTOERROR(result, "Copying live image failed/cancelled."); | |
| } else { | |
| bool unpackResult = dlg->UnpackFile(dlg->m_localFile, (usbFilesPath + ENDLESS_IMG_FILE_NAME), 0, ImageUnpackCallback, &dlg->m_cancelImageUnpack); | |
| IFFALSE_GOTOERROR(unpackResult, "Error unpacking image to USB drive"); | |
| } | |
| IFFALSE_GOTOERROR(0 != CopyFile(dlg->m_unpackedImageSig, usbFilesPath + CSTRING_GET_LAST(dlg->m_unpackedImageSig, '\\'), FALSE), "Error copying image signature file to drive."); | |
| FILE *liveFile; | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&liveFile, usbFilesPath + EXFAT_ENDLESS_LIVE_FILE_NAME, L"w"), "Error creating empty live file."); | |
| fwrite((const char*)originalImgFilename, 1, originalImgFilename.GetLength(), liveFile); | |
| fclose(liveFile); | |
| // copy grub to USB drive | |
| IFFALSE_GOTOERROR(CopyMultipleItems(fromFolder + GRUB_BOOT_SUBDIRECTORY, usbFilesPath), "Error copying grub folder to USB drive."); | |
| IFFALSE_GOTOERROR(0 != CopyFile(dlg->m_bootArchive, usbFilesPath + CSTRING_GET_LAST(dlg->m_bootArchive, '\\'), FALSE), "Error copying boot.zip file to drive."); | |
| IFFALSE_GOTOERROR(0 != CopyFile(dlg->m_bootArchiveSig, usbFilesPath + CSTRING_GET_LAST(dlg->m_bootArchiveSig, '\\'), FALSE), "Error copying boot.zip signature file to drive."); | |
| DWORD attributes = FILE_ATTRIBUTE_READONLY; | |
| IFFALSE_PRINTERROR(SetAttributesForFilesInFolder(usbFilesPath, attributes), "Error on SetFileAttributes"); | |
| IFFALSE_GOTOERROR(0 != CopyFile(exePath, driveLetter + CSTRING_GET_LAST(exePath, '\\'), FALSE), "Error copying installer binary to drive."); | |
| retResult = true; | |
| error: | |
| return retResult; | |
| } | |
| bool CEndlessUsbToolDlg::CopyMultipleItems(const CString &from, const CString &to) | |
| { | |
| FUNCTION_ENTER; | |
| SHFILEOPSTRUCT fileOperation; | |
| wchar_t fromPath[MAX_PATH + 1], toPath[MAX_PATH + 1]; | |
| uprintf("Copying '%ls' to '%ls'", from, to); | |
| memset(fromPath, 0, sizeof(fromPath)); | |
| wsprintf(fromPath, L"%ls", from); | |
| memset(toPath, 0, sizeof(toPath)); | |
| wsprintf(toPath, L"%ls", to); | |
| fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR; | |
| fileOperation.pFrom = fromPath; | |
| fileOperation.pTo = toPath; | |
| fileOperation.hwnd = NULL; | |
| fileOperation.wFunc = FO_COPY; | |
| int result = SHFileOperation(&fileOperation); | |
| return result == 0; | |
| } | |
| bool CEndlessUsbToolDlg::WriteMBRAndSBRToUSB(HANDLE hPhysical, const CString &bootFilesPath, DWORD bytesPerSector) | |
| { | |
| FUNCTION_ENTER; | |
| FAKE_FD fake_fd = { 0 }; | |
| FILE* fp = (FILE*)&fake_fd; | |
| FILE *bootImgFile = NULL, *coreImgFile = NULL; | |
| CString bootImgFilePath = bootFilesPath + LIVE_BOOT_IMG_FILE; | |
| CString coreImgFilePath = bootFilesPath + LIVE_CORE_IMG_FILE; | |
| unsigned char endlessMBRData[MAX_BOOT_IMG_FILE_SIZE + 1]; | |
| unsigned char *endlessSBRData = NULL; | |
| bool retResult = false; | |
| size_t countRead, coreImgSize; | |
| size_t mbrPartitionStart = bytesPerSector * MBR_PART_STARTING_SECTOR; | |
| // Load boot.img from file | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&bootImgFile, bootImgFilePath, L"rb"), "Error opening boot.img file"); | |
| countRead = fread(endlessMBRData, 1, sizeof(endlessMBRData), bootImgFile); | |
| IFFALSE_GOTOERROR(countRead == MAX_BOOT_IMG_FILE_SIZE, "Size of boot.img is not what is expected."); | |
| // write boot.img to USB drive | |
| fake_fd._handle = (char*)hPhysical; | |
| set_bytes_per_sector(SelectedDrive.Geometry.BytesPerSector); | |
| IFFALSE_GOTOERROR(write_data(fp, 0x0, endlessMBRData, MAX_BOOT_IMG_FILE_SIZE) != 0, "Error on write_data with boot.img contents."); | |
| // Read core.img data and write it to USB drive | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&coreImgFile, coreImgFilePath, L"rb"), "Error opening core.img file"); | |
| fseek(coreImgFile, 0L, SEEK_END); | |
| coreImgSize = ftell(coreImgFile); | |
| rewind(coreImgFile); | |
| uprintf("Size of SBR is %d bytes from %ls", coreImgSize, coreImgFilePath); | |
| IFFALSE_GOTOERROR(coreImgSize <= MBR_PART_LENGTH_BYTES, "Error: SBR found in core.img is too big."); | |
| endlessSBRData = (unsigned char*)malloc(bytesPerSector); | |
| coreImgSize = 0; | |
| while (!feof(coreImgFile) && coreImgSize < MBR_PART_LENGTH_BYTES) { | |
| countRead = fread(endlessSBRData, 1, bytesPerSector, coreImgFile); | |
| IFFALSE_GOTOERROR(write_data(fp, mbrPartitionStart + coreImgSize, endlessSBRData, countRead) != 0, "Error on write data with core.img contents."); | |
| coreImgSize += countRead; | |
| uprintf("Wrote %d bytes", coreImgSize); | |
| } | |
| retResult = true; | |
| DWORD size; | |
| // Tell the system we've updated the disk properties | |
| if (!DeviceIoControl(hPhysical, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &size, NULL)) | |
| uprintf("Failed to notify system about disk properties update: %s\n", WindowsErrorString()); | |
| error: | |
| safe_closefile(bootImgFile); | |
| safe_closefile(coreImgFile); | |
| if (endlessSBRData != NULL) { | |
| safe_free(endlessSBRData); | |
| } | |
| return retResult; | |
| } | |
| // Below defines need to be in this order | |
| #define DB_PROGRESS_CHECK_PARTITION 1 | |
| #define DB_PROGRESS_UNPACK_BOOT_ZIP 3 | |
| #define DB_PROGRESS_FINISHED_UNPACK 95 | |
| #define DB_PROGRESS_COPY_GRUB_FOLDER 98 | |
| #define DB_PROGRESS_MBR_OR_EFI_SETUP 100 | |
| #define REGKEY_UTC_TIME_PATH L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation" | |
| #define REGKEY_UTC_TIME L"RealTimeIsUniversal" | |
| #define REGKEY_FASTBOOT_PATH L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power" | |
| #define REGKEY_FASTBOOT L"HiberbootEnabled" | |
| DWORD WINAPI CEndlessUsbToolDlg::SetupDualBoot(LPVOID param) | |
| { | |
| FUNCTION_ENTER; | |
| CEndlessUsbToolDlg *dlg = (CEndlessUsbToolDlg*)param; | |
| CString systemDriveLetter; | |
| CString endlessFilesPath; | |
| CString endlessImgPath; | |
| CString bootFilesPath = CEndlessUsbToolApp::TempFilePath(CString(BOOT_COMPONENTS_FOLDER)) + L"\\"; | |
| CStringW exeFilePath = GetExePath(); | |
| const bool isBIOS = IsLegacyBIOSBoot(); | |
| systemDriveLetter = GetSystemDrive(); | |
| endlessFilesPath = systemDriveLetter + PATH_ENDLESS_SUBDIRECTORY; | |
| endlessImgPath = endlessFilesPath + ENDLESS_IMG_FILE_NAME; | |
| // Double-check this is an NTFS drive and (on BIOS systems) that there's no | |
| // non-Windows bootloader. Note that we deliberately jump to 'done' in this | |
| // case rather than 'error' -- we have not yet written anything to | |
| // endlessFilesPath so we shouldn't try to delete it, and we set | |
| // m_lastErrorCause directly. | |
| IFFALSE_GOTO(CanInstallToDrive(systemDriveLetter, isBIOS, dlg->m_lastErrorCause), "Can't install to this drive", done); | |
| UpdateProgress(OP_SETUP_DUALBOOT, DB_PROGRESS_CHECK_PARTITION); | |
| IFFALSE_PRINTERROR(ChangeAccessPermissions(endlessFilesPath, false), "Error on granting Endless files permissions."); | |
| // Unpack boot components | |
| IFFALSE_GOTOERROR(UnpackBootComponents(dlg->m_bootArchive, bootFilesPath), "Error unpacking boot components."); | |
| UpdateProgress(OP_SETUP_DUALBOOT, DB_PROGRESS_UNPACK_BOOT_ZIP); | |
| CHECK_IF_CANCELLED; | |
| // Create endless folder | |
| int createDirResult = SHCreateDirectoryExW(NULL, endlessFilesPath, NULL); | |
| IFFALSE_GOTOERROR(createDirResult == ERROR_SUCCESS || createDirResult == ERROR_ALREADY_EXISTS, "Error creating directory on system drive."); | |
| // Check folder is not set to compressed | |
| { | |
| CString folderName = CSTRING_GET_PATH(endlessFilesPath, L'\\'); | |
| IFFALSE_GOTOERROR(EnsureUncompressed(folderName), "Error setting folder to uncompressed."); | |
| } | |
| // Copy ourseleves to the <SYSDRIVE>:\endless directory | |
| IFFALSE_GOTOERROR(0 != CopyFile(exeFilePath, endlessFilesPath + ENDLESS_UNINSTALLER_NAME, FALSE), "Error copying exe uninstaller file."); | |
| // Add uninstall entry for Control Panel | |
| IFFALSE_GOTOERROR(AddUninstallRegistryKeys(endlessFilesPath + ENDLESS_UNINSTALLER_NAME, endlessFilesPath), "Error on AddUninstallRegistryKeys."); | |
| CEndlessUsbToolDlg::ImageUnpackOperation = OP_SETUP_DUALBOOT; | |
| CEndlessUsbToolDlg::ImageUnpackPercentStart = DB_PROGRESS_UNPACK_BOOT_ZIP; | |
| CEndlessUsbToolDlg::ImageUnpackPercentEnd = DB_PROGRESS_FINISHED_UNPACK; | |
| CEndlessUsbToolDlg::ImageUnpackFileSize = dlg->m_selectedFileSize; | |
| if (CSTRING_GET_LAST(dlg->m_localFile, '\\') == ENDLESS_IMG_FILE_NAME) { | |
| BOOL result = CopyFileEx(dlg->m_localFile, endlessImgPath, CEndlessUsbToolDlg::CopyProgressRoutine, dlg, NULL, 0); | |
| IFFALSE_GOTOERROR(result, "Copying unpacked dual-boot image failed/cancelled."); | |
| // Clear READONLY flag inherited from endless.img on live USB. | |
| IFFALSE_PRINTERROR(SetFileAttributes(endlessImgPath, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN), | |
| "Failed to clear FILE_ATTRIBUTE_READONLY; ExtendImageFile will probably now fail"); | |
| } else { | |
| // Unpack img file | |
| bool unpackResult = dlg->UnpackFile(dlg->m_localFile, endlessImgPath, 0, ImageUnpackCallback, &dlg->m_cancelImageUnpack); | |
| IFFALSE_GOTOERROR(unpackResult, "Error unpacking image to endless folder."); | |
| } | |
| // extend this file so it reaches the required size | |
| IFFALSE_GOTOERROR(ExtendImageFile(endlessImgPath, dlg->m_nrGigsSelected), "Error extending Endless image file"); | |
| UpdateProgress(OP_SETUP_DUALBOOT, DB_PROGRESS_FINISHED_UNPACK); | |
| CHECK_IF_CANCELLED; | |
| // Copy grub | |
| IFFALSE_GOTOERROR(CopyMultipleItems(bootFilesPath + GRUB_BOOT_SUBDIRECTORY, endlessFilesPath), "Error copying grub folder to system drive."); | |
| UpdateProgress(OP_SETUP_DUALBOOT, DB_PROGRESS_COPY_GRUB_FOLDER); | |
| CHECK_IF_CANCELLED; | |
| if (isBIOS) { | |
| IFFALSE_GOTOERROR(WriteMBRAndSBRToWinDrive(dlg, systemDriveLetter, bootFilesPath, endlessFilesPath), "Error on WriteMBRAndSBRToWinDrive"); | |
| } else { | |
| IFFALSE_GOTOERROR(SetupEndlessEFI(systemDriveLetter, bootFilesPath), "Error on SetupEndlessEFI"); | |
| } | |
| // set Endless file permissions | |
| IFFALSE_PRINTERROR(ChangeAccessPermissions(endlessFilesPath, true), "Error on setting Endless files permissions."); | |
| // set the registry key for Windows clock in UTC | |
| IFFALSE_PRINTERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UTC_TIME_PATH, REGKEY_UTC_TIME, CComVariant(1)), "Error on setting Windows clock to UTC."); | |
| // disable Fast Start (aka Hiberboot) so that the NTFS partition is always cleanly unmounted at shutdown: | |
| IFFALSE_PRINTERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_FASTBOOT_PATH, REGKEY_FASTBOOT, CComVariant(0)), "Error on disabling fastboot."); | |
| UpdateProgress(OP_SETUP_DUALBOOT, DB_PROGRESS_MBR_OR_EFI_SETUP); | |
| goto done; | |
| error: | |
| if (GetLastError() == ERROR_DISK_FULL) { | |
| dlg->m_lastErrorCause = ErrorCause_t::ErrorCauseInstallFailedDiskFull; | |
| } | |
| uprintf("SetupDualBoot exited with error."); | |
| if (dlg->m_lastErrorCause == ErrorCause_t::ErrorCauseNone) { | |
| dlg->m_lastErrorCause = ErrorCause_t::ErrorCauseWriteFailed; | |
| } | |
| RemoveNonEmptyDirectory(endlessFilesPath); | |
| done: | |
| RemoveNonEmptyDirectory(bootFilesPath); | |
| dlg->PostMessage(WM_FINISHED_ALL_OPERATIONS, 0, 0); | |
| return 0; | |
| } | |
| bool CEndlessUsbToolDlg::EnsureUncompressed(const CString &filePath) | |
| { | |
| FUNCTION_ENTER; | |
| bool success = false; | |
| HANDLE hFile = INVALID_HANDLE_VALUE; | |
| USHORT compression; | |
| DWORD size = 0; | |
| BOOL result; | |
| hFile = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
| IFFALSE_GOTOERROR(hFile != INVALID_HANDLE_VALUE, "CreateFile returned INVALID_HANDLE_VALUE."); | |
| result = DeviceIoControl(hFile, FSCTL_GET_COMPRESSION, NULL, 0, &compression, sizeof(compression), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying compression status."); | |
| uprintf("Path %ls has compression status %hu.", filePath, compression); | |
| if (compression != COMPRESSION_FORMAT_NONE) { | |
| compression = COMPRESSION_FORMAT_NONE; | |
| result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, &compression, sizeof(compression), NULL, 0, &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0, "Error on setting compression status."); | |
| uprintf ("Set %ls to uncompressed.", filePath); | |
| } | |
| success = true; | |
| error: | |
| safe_closehandle(hFile); | |
| return success; | |
| } | |
| bool CEndlessUsbToolDlg::ExtendImageFile(const CString &endlessImgPath, ULONGLONG selectedGigs) | |
| { | |
| FUNCTION_ENTER; | |
| HANDLE endlessImage = INVALID_HANDLE_VALUE; | |
| HANDLE hToken = INVALID_HANDLE_VALUE; | |
| bool retResult = false; | |
| // We need to have the MANAGE_VOLUME privilege to use SetFileValidData. | |
| // This lets us mark the extra blocks allocated by SetEndOfFile as | |
| // initialized, without actually writing 100Gb of zeroes. | |
| IFFALSE_GOTOERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_QUERY_SOURCE, &hToken) != 0, "OpenProcessToken failed"); | |
| IFFALSE_GOTOERROR(SetPrivilege(hToken, SE_MANAGE_VOLUME_NAME, TRUE), "Can't take MANAGE_VOLUME privilege. You must be logged on as Administrator."); | |
| endlessImage = CreateFile(endlessImgPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
| IFFALSE_GOTOERROR(endlessImage != INVALID_HANDLE_VALUE, "Error opening Endless image file."); | |
| LARGE_INTEGER lCurrentSize; | |
| IFFALSE_GOTOERROR(GetFileSizeEx(endlessImage, &lCurrentSize), "Error getting Endless image file size"); | |
| LARGE_INTEGER lsize; | |
| lsize.QuadPart = selectedGigs * BYTES_IN_GIGABYTE; | |
| uprintf("Trying to extend Endless image from %I64i bytes to %I64i bytes which is about %s", lCurrentSize.QuadPart, lsize.QuadPart, SizeToHumanReadable(lsize.QuadPart, FALSE, use_fake_units)); | |
| IFFALSE_GOTOERROR(SetFilePointerEx(endlessImage, lsize, NULL, FILE_BEGIN) != 0, "Error on SetFilePointerEx"); | |
| IFFALSE_GOTOERROR(SetEndOfFile(endlessImage), "Error on SetEndOfFile"); | |
| IFFALSE_GOTOERROR(SetFileValidData(endlessImage, lsize.QuadPart), "Error on SetFileValidData"); | |
| uprintf("Extended Endless image"); | |
| IFFALSE_PRINTERROR(SetPrivilege(hToken, SE_MANAGE_VOLUME_NAME, FALSE), "Can't release MANAGE_VOLUME privilege."); | |
| retResult = true; | |
| error: | |
| safe_closehandle(endlessImage); | |
| safe_closehandle(hToken); | |
| return retResult; | |
| } | |
| bool CEndlessUsbToolDlg::UnpackBootComponents(const CString &bootFilesPathGz, const CString &bootFilesPath) | |
| { | |
| FUNCTION_ENTER; | |
| bool retResult = false; | |
| RemoveNonEmptyDirectory(bootFilesPath); | |
| int createDirResult = SHCreateDirectoryExW(NULL, bootFilesPath, NULL); | |
| IFFALSE_GOTOERROR(createDirResult == ERROR_SUCCESS || createDirResult == ERROR_ALREADY_EXISTS, "Error creating local directory to unpack boot components."); | |
| IFFALSE_GOTOERROR(UnpackZip(CComBSTR(bootFilesPathGz), CComBSTR(bootFilesPath)), "Error unpacking archive to local folder."); | |
| retResult = true; | |
| error: | |
| return retResult; | |
| } | |
| bool CEndlessUsbToolDlg::IsLegacyBIOSBoot() | |
| { | |
| FUNCTION_ENTER; | |
| // From https://msdn.microsoft.com/en-us/library/windows/desktop/ms724325(v=vs.85).aspx | |
| // On a legacy BIOS-based system, or on a system that supports both legacy BIOS and UEFI where Windows was installed using legacy BIOS, | |
| // the function will fail with ERROR_INVALID_FUNCTION. On a UEFI-based system, the function will fail with an error specific to the firmware, | |
| // such as ERROR_NOACCESS, to indicate that the dummy GUID namespace does not exist. | |
| DWORD result = GetFirmwareEnvironmentVariable(L"", L"{00000000-0000-0000-0000-000000000000}", NULL, 0); | |
| return result == 0 && GetLastError() == ERROR_INVALID_FUNCTION; | |
| } | |
| // Checks preconditions for installing Endless OS to a given drive. Guaranteed to only modify 'cause' on error. | |
| bool CEndlessUsbToolDlg::CanInstallToDrive(const CString & systemDriveLetter, const bool isBIOS, ErrorCause &cause) | |
| { | |
| FUNCTION_ENTER; | |
| wchar_t fileSystemType[MAX_PATH + 1]; | |
| IFFALSE_GOTOERROR(GetVolumeInformation(systemDriveLetter, NULL, 0, NULL, NULL, NULL, fileSystemType, MAX_PATH + 1), "Error on GetVolumeInformation"); | |
| uprintf("File system type '%ls'", fileSystemType); | |
| if (0 != wcscmp(fileSystemType, L"NTFS")) { | |
| cause = ErrorCauseNotNTFS; | |
| return false; | |
| } | |
| if (!isBIOS) { | |
| if (!is_x64()) { | |
| uprintf("EFI system with 32-bit Windows; assuming IA32 EFI (which is unsupported)"); | |
| cause = ErrorCause32BitEFI; | |
| return false; | |
| } else { | |
| uprintf("EFI system with 64-bit Windows; assuming x64 EFI (which is ok)"); | |
| // assume there will be no problem installing GRUB | |
| return true; | |
| } | |
| } | |
| if (CEndlessUsbToolApp::m_enableOverwriteMbr) { | |
| uprintf("Not checking for Windows MBR as /forcembr is enabled."); | |
| return true; | |
| } else { | |
| bool ret = false; | |
| HANDLE hPhysical = GetPhysicalFromDriveLetter(systemDriveLetter); | |
| if (hPhysical != INVALID_HANDLE_VALUE) { | |
| FAKE_FD fake_fd = { 0 }; | |
| FILE* fp = (FILE*)&fake_fd; | |
| fake_fd._handle = (char*)hPhysical; | |
| if (IsWindowsMBR(fp, systemDriveLetter)) { | |
| ret = true; | |
| } else { | |
| cause = ErrorCauseNonWindowsMBR; | |
| } | |
| } else { | |
| PRINT_ERROR_MSG("can't check MBR, assuming non-Windows"); | |
| cause = ErrorCauseNonWindowsMBR; | |
| } | |
| safe_closehandle(hPhysical); | |
| return ret; | |
| } | |
| error: | |
| // Something unexpected happened happened | |
| cause = ErrorCauseWriteFailed; | |
| return false; | |
| } | |
| // Returns TRUE if the drive has a Windows MBR, FALSE otherwise | |
| bool CEndlessUsbToolDlg::IsWindowsMBR(FILE* fpDrive, const CString &TargetName) | |
| { | |
| FUNCTION_ENTER; | |
| const struct {int (*fn)(FILE *fp); char* str;} windows_mbr[] = { | |
| { is_2000_mbr, "Windows 2000/XP/2003" }, | |
| { is_vista_mbr, "Windows Vista" }, | |
| { is_win7_mbr, "Windows 7" }, | |
| }; | |
| int i; | |
| set_bytes_per_sector(SelectedDrive.Geometry.BytesPerSector); | |
| for (i=0; i < ARRAYSIZE(windows_mbr); i++) { | |
| if (windows_mbr[i].fn(fpDrive)) { | |
| uprintf("%ls has a %s MBR\n", TargetName, windows_mbr[i].str); | |
| return TRUE; | |
| } | |
| } | |
| uprintf("%ls has a non-Windows MBR\n", TargetName); | |
| return FALSE; | |
| } | |
| /* Handle nonstandard sector sizes (such as 4K) by writing | |
| the boot marker at every 512-2 bytes location */ | |
| static int write_bootmark(FILE *fp, unsigned long ulBytesPerSector) | |
| { | |
| unsigned char aucRef[] = { 0x55, 0xAA }; | |
| unsigned long pos = 0x1FE; | |
| for (pos = 0x1FE; pos < ulBytesPerSector; pos += 0x200) { | |
| if (!write_data(fp, pos, aucRef, sizeof(aucRef))) | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| // Unconditionally write GRUB from 'bootFilesPath' to 'systemDriveLetter', backing up the current boot track to 'endlessFilesPath'. | |
| bool CEndlessUsbToolDlg::WriteMBRAndSBRToWinDrive(CEndlessUsbToolDlg *dlg, const CString &systemDriveLetter, const CString &bootFilesPath, const CString &endlessFilesPath) | |
| { | |
| FUNCTION_ENTER; | |
| bool retResult = false; | |
| HANDLE hPhysical = INVALID_HANDLE_VALUE; | |
| BYTE geometry[256] = { 0 }; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| BYTE layout[4096] = { 0 }; | |
| PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | |
| DWORD size; | |
| BOOL result; | |
| CString coreImgFilePath = bootFilesPath + NTFS_CORE_IMG_FILE; | |
| FILE *bootImgFile = NULL, *boottrackImgFile = NULL; | |
| FAKE_FD fake_fd = { 0 }; | |
| FILE* fp = (FILE*)&fake_fd; | |
| CString bootImgFilePath = bootFilesPath + NTFS_BOOT_IMG_FILE; | |
| unsigned char endlessMBRData[MAX_BOOT_IMG_FILE_SIZE]; | |
| size_t countRead; | |
| unsigned char *boottrackData = NULL; | |
| // Get system disk handle | |
| hPhysical = GetPhysicalFromDriveLetter(systemDriveLetter); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| // get disk geometry information | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk geometry."); | |
| set_bytes_per_sector(DiskGeometry->Geometry.BytesPerSector); | |
| // Check that SBR will "fit" before writing to disk | |
| // Jira issue: https://movial.atlassian.net/browse/EOIFT-158 | |
| // This will be done in grub_util_bios_setup, here just get the last available sector for writing the SBR between MBR and first partition | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk layout."); | |
| IFFALSE_GOTOERROR(DriveLayout->PartitionCount > 0, "We don't have any partitions?"); | |
| uprintf("BytesPerSector=%d, PartitionEntry[0].StartingOffset=%I64i", DiskGeometry->Geometry.BytesPerSector, DriveLayout->PartitionEntry[0].StartingOffset.QuadPart); | |
| // preserve boot track before writing anything to disk | |
| // Jira https://movial.atlassian.net/browse/EOIFT-169 | |
| LONGLONG minStartingOffset = MAX_BOOTTRACK_SIZE; | |
| PARTITION_INFORMATION_EX *partition = NULL; | |
| for (DWORD index = 0; index < DriveLayout->PartitionCount; index++) { | |
| partition = &(DriveLayout->PartitionEntry[index]); | |
| IFFALSE_GOTOERROR(partition->PartitionStyle == PARTITION_STYLE_MBR, "non-MBR partition in supposedly-MBR table(?)"); | |
| if (partition->Mbr.PartitionType == PARTITION_ENTRY_UNUSED) { | |
| uprintf("Partition %d is unused", index); | |
| } else { | |
| uprintf("Partition %d starting offset = %I64i", index, partition->StartingOffset.QuadPart); | |
| minStartingOffset = min(minStartingOffset, partition->StartingOffset.QuadPart); | |
| } | |
| } | |
| dlg->TrackEvent(_T("BootTrackSize"), minStartingOffset); | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&boottrackImgFile, endlessFilesPath + BACKUP_BOOTTRACK_IMG, L"wb"), "Error opening boottrack.img file"); | |
| boottrackData = (unsigned char*)malloc(DiskGeometry->Geometry.BytesPerSector); | |
| LONGLONG boottrackNrSectors = minStartingOffset / DiskGeometry->Geometry.BytesPerSector; | |
| uprintf("Trying to save %I64i sectors in file %ls", boottrackNrSectors, endlessFilesPath + BACKUP_BOOTTRACK_IMG); | |
| for (LONGLONG i = 0; i < boottrackNrSectors; i++) { | |
| int64_t result = read_sectors(hPhysical, DiskGeometry->Geometry.BytesPerSector, i, 1, boottrackData); | |
| IFFALSE_GOTOERROR(result == DiskGeometry->Geometry.BytesPerSector, "Error reading from disk."); | |
| IFFALSE_GOTOERROR(fwrite(boottrackData, 1, DiskGeometry->Geometry.BytesPerSector, boottrackImgFile) == DiskGeometry->Geometry.BytesPerSector, "Error writing data to boottrack.img."); | |
| } | |
| safe_closefile(boottrackImgFile); | |
| // Write core.img | |
| IFFALSE_GOTOERROR(grub_util_bios_setup(coreImgFilePath, hPhysical, boottrackNrSectors), "Error writing SBR to disk"); | |
| // Load boot.img from file | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&bootImgFile, bootImgFilePath, L"rb"), "Error opening boot.img file"); | |
| countRead = fread(endlessMBRData, 1, MAX_BOOT_IMG_FILE_SIZE, bootImgFile); | |
| IFFALSE_GOTOERROR(countRead == MAX_BOOT_IMG_FILE_SIZE, "Size of boot.img is not what is expected. Not enough data to read"); | |
| // Also, ensure we apply the workaround for stupid BIOSes: https://github.com/endlessm/grub/blob/master/util/setup.c#L384 | |
| // use boot.img from boot.zip rather than rufus - https://movial.atlassian.net/browse/EOIFT-170 | |
| unsigned char *boot_drive_check = (unsigned char *)(endlessMBRData + GRUB_BOOT_MACHINE_DRIVE_CHECK); | |
| /* Replace the jmp (2 bytes) with double nop's. */ | |
| boot_drive_check[0] = 0x90; | |
| boot_drive_check[1] = 0x90; | |
| // reusing boottrackImgFile, saving mbr.img for uninstall purposes | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&boottrackImgFile, endlessFilesPath + BACKUP_MBR_IMG, L"wb"), "Error opening mbr.img file"); | |
| fwrite(endlessMBRData, 1, MBR_WINDOWS_NT_MAGIC, boottrackImgFile); | |
| safe_closefile(boottrackImgFile); | |
| // write boot.img to disk | |
| fake_fd._handle = (char*)hPhysical; | |
| set_bytes_per_sector(SelectedDrive.Geometry.BytesPerSector); | |
| IFFALSE_GOTOERROR(write_data(fp, 0x0, endlessMBRData, MBR_WINDOWS_NT_MAGIC) != 0, "Error on write_data with boot.img contents."); | |
| IFFALSE_PRINTERROR(write_bootmark(fp, DiskGeometry->Geometry.BytesPerSector), "Error on write_bootmark"); | |
| retResult = true; | |
| error: | |
| safe_closehandle(hPhysical); | |
| safe_closefile(bootImgFile); | |
| safe_closefile(boottrackImgFile); | |
| if (boottrackData != NULL) safe_free(boottrackData); | |
| return retResult; | |
| } | |
| bool CEndlessUsbToolDlg::SetupEndlessEFI(const CString &systemDriveLetter, const CString &bootFilesPath) | |
| { | |
| FUNCTION_ENTER; | |
| HANDLE hPhysical; | |
| bool retResult = false; | |
| CString windowsEspDriveLetter; | |
| const char *espMountLetter = NULL; | |
| hPhysical = GetPhysicalFromDriveLetter(systemDriveLetter); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| IFFALSE_GOTOERROR(MountESPFromDrive(hPhysical, &espMountLetter, systemDriveLetter), "Error on MountESPFromDrive"); | |
| windowsEspDriveLetter = UTF8ToCString(espMountLetter); | |
| IFFALSE_GOTOERROR(CopyMultipleItems(bootFilesPath + EFI_BOOT_SUBDIRECTORY + L"\\" + ALL_FILES, windowsEspDriveLetter + L"\\" + ENDLESS_BOOT_SUBDIRECTORY), "Error copying EFI folder to Windows ESP partition."); | |
| IFFALSE_PRINTERROR(EFIRequireNeededPrivileges(), "Error on EFIRequireNeededPrivileges."); | |
| IFFALSE_GOTOERROR(EFICreateNewEntry(windowsEspDriveLetter, CString(L"\\") + ENDLESS_BOOT_SUBDIRECTORY + L"\\" + ENDLESS_BOOT_EFI_FILE, ENDLESS_OS_NAME), "Error on EFICreateNewEntry"); | |
| retResult = true; | |
| error: | |
| safe_closehandle(hPhysical); | |
| if (espMountLetter != NULL) AltUnmountVolume(espMountLetter); | |
| return retResult; | |
| } | |
| HANDLE CEndlessUsbToolDlg::GetPhysicalFromDriveLetter(const CString &driveLetter) | |
| { | |
| FUNCTION_ENTER; | |
| HANDLE hPhysical = INVALID_HANDLE_VALUE; | |
| CStringA logical_drive; | |
| logical_drive.Format("\\\\.\\%lc:", driveLetter[0]); | |
| hPhysical = CreateFileA(logical_drive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "CreateFileA call returned invalid handle."); | |
| int drive_number = GetDriveNumber(hPhysical, logical_drive.GetBuffer()); | |
| drive_number += DRIVE_INDEX_MIN; | |
| safe_closehandle(hPhysical); | |
| hPhysical = GetPhysicalHandle(drive_number, TRUE, TRUE); | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| error: | |
| return hPhysical; | |
| } | |
| bool CEndlessUsbToolDlg::Has64BitSupport() | |
| { | |
| // MSDN: https://msdn.microsoft.com/en-us/library/hskdteyh%28v=vs.100%29.aspx?f=255&MSPPError=-2147217396 | |
| int CPUInfo[4]; | |
| // first check if extended data is available, which is always true on 64-bit | |
| __cpuid(CPUInfo, 0x80000000); | |
| if (!(CPUInfo[0] & 0x80000000)) | |
| return false; | |
| // request extended data, bit 29 is set on 64-bit systems | |
| __cpuid(CPUInfo, 0x80000001); | |
| if (CPUInfo[3] & 0x20000000) | |
| return true; | |
| return false; | |
| } | |
| /* Protect or unprotect files in 'path' (C:\endless or $eoslive:\endless) from modification, | |
| *non-*-recursively. | |
| */ | |
| BOOL CEndlessUsbToolDlg::SetAttributesForFilesInFolder(CString path, DWORD attributes) | |
| { | |
| WIN32_FIND_DATA findFileData; | |
| HANDLE findFilesHandle = FindFirstFile(path + ALL_FILES, &findFileData); | |
| uprintf("SetAccessToFilesInFolder(L\"%ls\", %x)", path, attributes); | |
| DWORD folderAttributes = attributes & ~FILE_ATTRIBUTE_HIDDEN; | |
| uprintf("SetFileAttributes(L\"%ls\", %x)", path, folderAttributes); | |
| IFFALSE_PRINTERROR(SetFileAttributes(path, folderAttributes) != 0, "Error on SetFileAttributes"); | |
| if (findFilesHandle == INVALID_HANDLE_VALUE) { | |
| uprintf("UpdateFileEntries: No files found in directory \"%ls\"", path); | |
| } else { | |
| do { | |
| if ((0 == wcscmp(findFileData.cFileName, L".") || 0 == wcscmp(findFileData.cFileName, L".."))) continue; | |
| if (0 != wcscmp(findFileData.cFileName, ENDLESS_UNINSTALLER_NAME)) { | |
| IFFALSE_PRINTERROR(SetFileAttributes(path + findFileData.cFileName, attributes) != 0, "Error on SetFileAttributes for child"); | |
| // We deliberately do not recurse into subdirectories. | |
| // | |
| // In Endless OS, files and directories marked +h/+s on NTFS/exFAT partitions (respectively) are | |
| // hidden from directory listings, but can be manipulated if you know the full path. If we mark | |
| // the *contents* of \endless\grub as hidden & system then it's impossible to remove them, | |
| // because we don't have a full list of its contents. | |
| // | |
| // If \endless\grub is hidden from listings but its contents are not, you can | |
| // rm -r $eoslive/endless/grub if you know its full path -- which we do. | |
| } | |
| } while (FindNextFile(findFilesHandle, &findFileData) != 0); | |
| FindClose(findFilesHandle); | |
| } | |
| return true; | |
| } | |
| BOOL CEndlessUsbToolDlg::SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) | |
| { | |
| TOKEN_PRIVILEGES tp, tpOld; | |
| DWORD tpOldLen = sizeof(TOKEN_PRIVILEGES); | |
| LUID luid; | |
| BOOL retResult = FALSE; | |
| IFFALSE_GOTOERROR(LookupPrivilegeValue(NULL, lpszPrivilege, &luid) != 0, "LookupPrivilegeValue error"); | |
| tp.PrivilegeCount = 1; | |
| tp.Privileges[0].Luid = luid; | |
| tp.Privileges[0].Attributes = bEnablePrivilege ? SE_PRIVILEGE_ENABLED : 0; | |
| IFFALSE_GOTOERROR(AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &tpOld, &tpOldLen) != 0, "AdjustTokenPrivileges error"); | |
| if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { | |
| IFFALSE_GOTOERROR( | |
| tpOld.PrivilegeCount == 1 && | |
| tpOld.Privileges[0].Luid.LowPart == luid.LowPart && | |
| tpOld.Privileges[0].Luid.HighPart == luid.HighPart, | |
| "AdjustTokenPrivileges returned ERROR_NOT_ALL_ASSIGNED, and the requested privilege was indeed not altered"); | |
| } | |
| retResult = TRUE; | |
| error: | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::ChangeAccessPermissions(CString path, bool restrictAccess) | |
| { | |
| LPWSTR pFilename = path.GetBuffer(); | |
| BOOL retResult = FALSE; | |
| PSID pSIDEveryone = NULL; | |
| PSID pSIDAdmin = NULL; | |
| SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; | |
| SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY; | |
| const int NUM_ACES = 2; | |
| EXPLICIT_ACCESS ea[NUM_ACES]; | |
| PACL pACL = NULL; | |
| HANDLE hToken = NULL; | |
| uprintf("RestrictFileAccess called with '%ls'", path); | |
| // Mark files as system and read-only | |
| if (restrictAccess) { | |
| DWORD attributes = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; | |
| IFFALSE_PRINTERROR(SetAttributesForFilesInFolder(path, attributes), "Error on SetAttributesForFilesInFolder"); | |
| } | |
| // Specify the DACL to use. | |
| // Create a SID for the Everyone group. | |
| IFFALSE_GOTOERROR(AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSIDEveryone), "AllocateAndInitializeSid (Everyone) error"); | |
| // Create a SID for the BUILTIN\Administrators group. | |
| IFFALSE_GOTOERROR(AllocateAndInitializeSid(&SIDAuthNT, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pSIDAdmin), "AllocateAndInitializeSid (Admin) error"); | |
| ZeroMemory(&ea, NUM_ACES * sizeof(EXPLICIT_ACCESS)); | |
| // Deny access for Everyone. | |
| ea[0].grfAccessPermissions = restrictAccess ? DELETE | FILE_DELETE_CHILD | FILE_WRITE_ATTRIBUTES : STANDARD_RIGHTS_ALL; | |
| ea[0].grfAccessMode = restrictAccess ? DENY_ACCESS : SET_ACCESS; | |
| ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; | |
| ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; | |
| ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; | |
| ea[0].Trustee.ptstrName = (LPTSTR)pSIDEveryone; | |
| // Set full control for Administrators. | |
| ea[1].grfAccessPermissions = GENERIC_ALL; | |
| ea[1].grfAccessMode = SET_ACCESS; | |
| ea[1].grfInheritance = NO_INHERITANCE; | |
| ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; | |
| ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; | |
| ea[1].Trustee.ptstrName = (LPTSTR)pSIDAdmin; | |
| IFFALSE_GOTOERROR(ERROR_SUCCESS == SetEntriesInAcl(NUM_ACES, ea, NULL, &pACL), "Failed SetEntriesInAcl"); | |
| // Try to modify the object's DACL. | |
| DWORD dwRes = SetNamedSecurityInfo( | |
| pFilename, // name of the object | |
| SE_FILE_OBJECT, // type of object | |
| DACL_SECURITY_INFORMATION, // change only the object's DACL | |
| NULL, NULL, // do not change owner or group | |
| pACL, // DACL specified | |
| NULL); // do not change SACL | |
| uprintf("Return value for first SetNamedSecurityInfo call %u", dwRes); | |
| IFTRUE_GOTO(ERROR_SUCCESS == dwRes, "Successfully changed DACL", done); | |
| IFFALSE_GOTOERROR(dwRes == ERROR_ACCESS_DENIED, "First SetNamedSecurityInfo call failed"); | |
| // If the preceding call failed because access was denied, | |
| // enable the SE_TAKE_OWNERSHIP_NAME privilege, create a SID for | |
| // the Administrators group, take ownership of the object, and | |
| // disable the privilege. Then try again to set the object's DACL. | |
| // Open a handle to the access token for the calling process. | |
| IFFALSE_GOTOERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken) != 0, "OpenProcessToken failed"); | |
| // Enable the SE_TAKE_OWNERSHIP_NAME privilege. | |
| IFFALSE_GOTOERROR(SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE), "You must be logged on as Administrator."); | |
| // Set the owner in the object's security descriptor. | |
| dwRes = SetNamedSecurityInfo( | |
| pFilename, // name of the object | |
| SE_FILE_OBJECT, // type of object | |
| OWNER_SECURITY_INFORMATION, // change only the object's owner | |
| pSIDAdmin, // SID of Administrator group | |
| NULL, | |
| NULL, | |
| NULL); | |
| uprintf("Return value for second SetNamedSecurityInfo call %u", dwRes); | |
| IFFALSE_GOTOERROR(ERROR_SUCCESS == dwRes, "Could not set owner."); | |
| // Disable the SE_TAKE_OWNERSHIP_NAME privilege. | |
| IFFALSE_GOTOERROR(SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, FALSE), "Failed SetPrivilege call unexpectedly."); | |
| // Try again to modify the object's DACL, now that we are the owner. | |
| dwRes = SetNamedSecurityInfo( | |
| pFilename, // name of the object | |
| SE_FILE_OBJECT, // type of object | |
| DACL_SECURITY_INFORMATION, // change only the object's DACL | |
| NULL, NULL, // do not change owner or group | |
| pACL, // DACL specified | |
| NULL); // do not change SACL | |
| uprintf("Return value for third SetNamedSecurityInfo call %u", dwRes); | |
| IFFALSE_GOTOERROR(ERROR_SUCCESS == dwRes, "Third SetNamedSecurityInfo call failed."); | |
| done: | |
| // Remove system and read-only from files | |
| if (!restrictAccess) { | |
| DWORD attributes = FILE_ATTRIBUTE_NORMAL; | |
| IFFALSE_PRINTERROR(SetAttributesForFilesInFolder(path, attributes), "Error on SetFileAttributes"); | |
| } | |
| retResult = TRUE; | |
| error: | |
| if (pSIDAdmin) FreeSid(pSIDAdmin); | |
| if (pSIDEveryone) FreeSid(pSIDEveryone); | |
| if (pACL) LocalFree(pACL); | |
| if (hToken) CloseHandle(hToken); | |
| return retResult; | |
| } | |
| CStringW CEndlessUsbToolDlg::GetSystemDrive() | |
| { | |
| CStringW systemDrive(L"C:\\"); | |
| wchar_t windowsPath[MAX_PATH]; | |
| HRESULT hr = SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, windowsPath); | |
| if (hr == S_OK) { | |
| systemDrive = windowsPath; | |
| systemDrive = systemDrive.Left(3); | |
| } else { | |
| uprintf("SHGetFolderPath returned %X", hr); | |
| } | |
| return systemDrive; | |
| } | |
| #define REGKEY_BACKUP_PREFIX L"EndlessBackup" | |
| #define REGKEY_DWORD_MISSING DWORD_MAX | |
| BOOL CEndlessUsbToolDlg::SetEndlessRegistryKey(HKEY parentKey, const CString &keyPath, const CString &keyName, CComVariant keyValue, bool createBackup) | |
| { | |
| CRegKey registryKey; | |
| LSTATUS result; | |
| CComVariant backupValue; | |
| uprintf("SetEndlessRegistryKey called with path '%ls' and key '%ls'", keyPath, keyName); | |
| result = registryKey.Open(parentKey, keyPath, KEY_ALL_ACCESS); | |
| IFFALSE_RETURN_VALUE(result == ERROR_SUCCESS, "Error opening registry key.", FALSE); | |
| backupValue.ChangeType(VT_NULL); | |
| switch (keyValue.vt) { | |
| case VT_I4: | |
| case VT_UI4: | |
| if (createBackup) { | |
| DWORD dwValue; | |
| result = registryKey.QueryDWORDValue(CString(REGKEY_BACKUP_PREFIX) + keyName, dwValue); | |
| if (result == ERROR_SUCCESS) { | |
| uprintf("There already is a backup created for '%ls' in '%ls'. Aborting creating a second backup", keyName, keyPath); | |
| createBackup = false; | |
| break; | |
| } | |
| result = registryKey.QueryDWORDValue(keyName, dwValue); | |
| if (result == ERROR_SUCCESS) backupValue = dwValue; | |
| else backupValue = REGKEY_DWORD_MISSING; | |
| } | |
| result = registryKey.SetDWORDValue(keyName, keyValue.intVal); | |
| break; | |
| case VT_BSTR: | |
| if (createBackup) { | |
| uprintf("Error: backup requested and not implemented for type VT_BSTR."); | |
| return FALSE; | |
| } | |
| result = registryKey.SetStringValue(keyName, keyValue.bstrVal); | |
| break; | |
| default: | |
| uprintf("ERROR: variant type %d not handled", keyValue.vt); | |
| return FALSE; | |
| } | |
| IFFALSE_RETURN_VALUE(result == ERROR_SUCCESS, "Error on setting key value", FALSE); | |
| if (createBackup && backupValue.vt != VT_NULL) { | |
| backupValue.vt = keyValue.vt; | |
| IFFALSE_PRINTERROR(SetEndlessRegistryKey(parentKey, keyPath, CString(REGKEY_BACKUP_PREFIX) + keyName, backupValue, false), "Error on creating backup key"); | |
| } | |
| return TRUE; | |
| } | |
| #pragma comment(lib, "wbemuuid.lib") | |
| static BOOL RunWMIQuery(const CString &objectPath, const CString &query, std::function< BOOL( CComPtr<IEnumWbemClassObject> & )> callback) | |
| { | |
| FUNCTION_ENTER; | |
| HRESULT hres; | |
| BOOL retResult = FALSE; | |
| CComPtr<IWbemLocator> pLoc; | |
| CComPtr<IWbemServices> pSvc; | |
| CComPtr<IEnumWbemClassObject> pEnumerator; | |
| CString bitlockerQuery; | |
| hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hres), "Error on CoInitializeEx."); | |
| hres = CoInitializeSecurity( | |
| NULL, | |
| -1, // COM authentication | |
| NULL, // Authentication services | |
| NULL, // Reserved | |
| RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication | |
| RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation | |
| NULL, // Authentication info | |
| EOAC_NONE, // Additional capabilities | |
| NULL // Reserved | |
| ); | |
| IFFALSE_PRINTERROR(SUCCEEDED(hres), "Error on CoInitializeSecurity."); | |
| // Obtain the initial locator to WMI | |
| hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hres), "Error on CoCreateInstance."); | |
| // Connect to WMI through the IWbemLocator::ConnectServer method | |
| // Connect to the root\cimv2 namespace with the current user and obtain pointer pSvc to make IWbemServices calls. | |
| hres = pLoc->ConnectServer( | |
| _bstr_t(objectPath), // Object path of WMI namespace | |
| NULL, // User name. NULL = current user | |
| NULL, // User password. NULL = current | |
| 0, // Locale. NULL indicates current | |
| NULL, // Security flags. | |
| 0, // Authority (for example, Kerberos) | |
| 0, // Context object | |
| &pSvc // pointer to IWbemServices proxy | |
| ); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hres), "Error on pLoc->ConnectServer."); | |
| // Set security levels on the proxy | |
| hres = CoSetProxyBlanket( | |
| pSvc, // Indicates the proxy to set | |
| RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx | |
| RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx | |
| NULL, // Server principal name | |
| RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx | |
| RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx | |
| NULL, // client identity | |
| EOAC_NONE // proxy capabilities | |
| ); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hres), "Error on CoSetProxyBlanket."); | |
| // Use the IWbemServices pointer to make requests of WMI | |
| hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t(query), WBEM_FLAG_FORWARD_ONLY | WBEM_RETURN_WHEN_COMPLETE, NULL, &pEnumerator); | |
| IFFALSE_GOTOERROR(SUCCEEDED(hres), "Error on pSvc->ExecQuery."); | |
| retResult = callback(pEnumerator); | |
| error: | |
| CoUninitialize(); | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::IsBitlockedDrive(const CString &drive) | |
| { | |
| FUNCTION_ENTER; | |
| const CString objectPath(L"ROOT\\CIMV2\\Security\\MicrosoftVolumeEncryption"); | |
| CString bitlockerQuery; | |
| bitlockerQuery.Format(L"Select * from Win32_EncryptableVolume where ProtectionStatus != 0 and DriveLetter = '%ls'", drive); | |
| BOOL retResult = RunWMIQuery(objectPath, bitlockerQuery, [](CComPtr<IEnumWbemClassObject> &pEnumerator) { | |
| HRESULT hres; | |
| CComPtr<IWbemClassObject> pclsObj; | |
| ULONG uReturned = 0; | |
| hres = pEnumerator->Next(0, 1, &pclsObj, &uReturned); | |
| uprintf("IsBitlockedDrive: hres=%x and uReturned=%d", hres, uReturned); | |
| return (hres == S_OK) && (uReturned == 1); | |
| }); | |
| uprintf("IsBitlockedDrive done with retResult=%d", retResult); | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::GetMachineInfo(CString &manufacturer, CString &model) | |
| { | |
| FUNCTION_ENTER; | |
| const CString objectPath(L"ROOT\\CIMV2"); | |
| const CString query(L"Select * from Win32_ComputerSystem"); | |
| return RunWMIQuery(objectPath, query, [&](CComPtr<IEnumWbemClassObject> &pEnumerator) { | |
| while (pEnumerator) | |
| { | |
| CComPtr<IWbemClassObject> pclsObj; | |
| ULONG uReturn = 0; | |
| HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, | |
| &pclsObj, &uReturn); | |
| if (0 == uReturn) | |
| { | |
| break; | |
| } | |
| VARIANT vtProp; | |
| hr = pclsObj->Get(L"Manufacturer", 0, &vtProp, 0, 0); | |
| manufacturer = vtProp.bstrVal; | |
| VariantClear(&vtProp); | |
| hr = pclsObj->Get(L"Model", 0, &vtProp, 0, 0); | |
| model = vtProp.bstrVal; | |
| VariantClear(&vtProp); | |
| } | |
| return TRUE; | |
| }); | |
| } | |
| CStringW CEndlessUsbToolDlg::GetExePath() | |
| { | |
| wchar_t *exePath = NULL; | |
| DWORD neededSize = MAX_PATH; | |
| CStringW retValue = L""; | |
| do { | |
| if (exePath != NULL) safe_free(exePath); | |
| exePath = (wchar_t*)malloc((neededSize + 1) * sizeof(wchar_t)); | |
| memset(exePath, 0, (neededSize + 1) * sizeof(wchar_t)); | |
| DWORD result = GetModuleFileNameW(NULL, exePath, neededSize); | |
| if (nWindowsVersion > WINDOWS_XP) { | |
| IFFALSE_GOTOERROR(result != 0 && (GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_INSUFFICIENT_BUFFER), "Could not get needed size for module path"); | |
| neededSize = result; | |
| IFFALSE_CONTINUE(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "Not enough memory allocated for buffer. Trying again."); | |
| } else { | |
| IFFALSE_GOTOERROR(result != 0, "Windows XP: Could not get needed size for module path"); | |
| } | |
| break; | |
| } while (TRUE); | |
| retValue = exePath; | |
| error: | |
| if (exePath != NULL) safe_free(exePath); | |
| return retValue; | |
| } | |
| #define REGKEY_WIN_UNINSTALL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" | |
| #define REGKEY_ENDLESS_OS ENDLESS_OS_NAME | |
| #define REGKEY_UNINSTALLENDLESS (REGKEY_WIN_UNINSTALL REGKEY_ENDLESS_OS) | |
| #define REGKEY_DISPLAYNAME L"DisplayName" | |
| #define REGKEY_UNINSTALL_STRING L"UninstallString" | |
| #define REGKEY_INSTALL_LOCATION L"InstallLocation" | |
| #define REGKEY_PUBLISHER L"Publisher" | |
| #define REGKEY_HELP_LINK L"HelpLink" | |
| #define REGKEY_NOCHANGE L"NoChange" | |
| #define REGKEY_NOMODIFY L"NoModify" | |
| #define REGKEY_DISPLAYNAME_TEXT ENDLESS_OS_NAME | |
| #define REGKEY_PUBLISHER_TEXT L"Endless Mobile, Inc." | |
| BOOL CEndlessUsbToolDlg::AddUninstallRegistryKeys(const CStringW &uninstallExePath, const CStringW &installPath) | |
| { | |
| CRegKey registryKey; | |
| LSTATUS result; | |
| BOOL retResult = FALSE; | |
| CStringW displayVersion; | |
| result = registryKey.Create(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS); | |
| uprintf("Result %d on creating key '%ls'", result, REGKEY_UNINSTALLENDLESS); | |
| IFFALSE_GOTOERROR(result == ERROR_SUCCESS, "Error on CRegKey::Create."); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_DISPLAYNAME, REGKEY_DISPLAYNAME_TEXT, false), "Error on REGKEY_DISPLAYNAME"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_HELP_LINK, UTF8ToBSTR(lmprintf(MSG_314)), false), "Error on REGKEY_HELP_LINK"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_UNINSTALL_STRING, CComBSTR(uninstallExePath), false), "Error on REGKEY_UNINSTALL_STRING"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_INSTALL_LOCATION, CComBSTR(installPath), false), "Error on REGKEY_INSTALL_LOCATION"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_PUBLISHER, REGKEY_PUBLISHER_TEXT, false), "Error on REGKEY_PUBLISHER"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_NOCHANGE, 1, false), "Error on REGKEY_NOCHANGE"); | |
| IFFALSE_GOTOERROR(SetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UNINSTALLENDLESS, REGKEY_NOMODIFY, 1, false), "Error on REGKEY_NOMODIFY"); | |
| retResult = TRUE; | |
| error: | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::UninstallDualBoot(CEndlessUsbToolDlg *dlg) | |
| { | |
| FUNCTION_ENTER; | |
| BOOL retResult = FALSE; | |
| uint32_t popupMsgId = MSG_363; | |
| UINT popupStyle = MB_OK | MB_ICONERROR; | |
| FILE *boottrackImgFile = NULL; | |
| CString systemDriveLetter = GetSystemDrive(); | |
| CString endlessFilesPath = systemDriveLetter + PATH_ENDLESS_SUBDIRECTORY; | |
| HANDLE hPhysical = GetPhysicalFromDriveLetter(systemDriveLetter); | |
| const char *espMountLetter = NULL; | |
| CString exePath = GetExePath(); | |
| if (IsLegacyBIOSBoot()) { // remove MBR entry | |
| FAKE_FD fake_fd = { 0 }; | |
| FILE* fp = (FILE*)&fake_fd; | |
| fake_fd._handle = (char*)hPhysical; | |
| IFFALSE_GOTOERROR(hPhysical != INVALID_HANDLE_VALUE, "Error on acquiring disk handle."); | |
| IFTRUE_GOTO(IsWindowsMBR(fp, systemDriveLetter), "Windows MBR detected so skipping MBR writing", done_with_mbr); | |
| if (!CEndlessUsbToolApp::m_enableOverwriteMbr && !IsEndlessMBR(fp, endlessFilesPath)) { | |
| dlg->m_lastErrorCause = ErrorCause_t::ErrorCauseNonEndlessMBR; | |
| goto error; | |
| } | |
| // restore boottrack.img if present and use embedded grub only as fallback | |
| IFTRUE_GOTO(RestoreOriginalBoottrack(endlessFilesPath, hPhysical, fp), "Success on restoring original boottrack", done_with_mbr); | |
| uprintf("Failure on restoring original boottrack, falling back to embedded MBRs."); | |
| if (nWindowsVersion >= WINDOWS_7) { | |
| IFFALSE_GOTOERROR(write_win7_mbr(fp), "Error on write_win7_mbr"); | |
| } else if(nWindowsVersion == WINDOWS_VISTA) { | |
| IFFALSE_GOTOERROR(write_vista_mbr(fp), "Error on write_vista_mbr"); | |
| } else if (nWindowsVersion == WINDOWS_2003 || nWindowsVersion == WINDOWS_XP) { | |
| IFFALSE_GOTOERROR(write_2000_mbr(fp), "Error on write_2000_mbr"); | |
| } else { | |
| uprintf("Restore MBR not supported for this windows version '%s', ver %d", WindowsVersionStr, nWindowsVersion); | |
| goto error; | |
| } | |
| } else { // remove EFI entry | |
| CString windowsEspDriveLetter; | |
| IFFALSE_PRINTERROR(EFIRequireNeededPrivileges(), "Error on EFIRequireNeededPrivileges."); | |
| IFFALSE_PRINTERROR(EFIRemoveEntry(ENDLESS_OS_NAME), "Error on EFIRemoveEntry, continuing with uninstall anyway."); | |
| IFFALSE_GOTOERROR(MountESPFromDrive(hPhysical, &espMountLetter, systemDriveLetter), "Error on MountESPFromDrive"); | |
| windowsEspDriveLetter = UTF8ToCString(espMountLetter); | |
| RemoveNonEmptyDirectory(windowsEspDriveLetter + L"\\" + ENDLESS_BOOT_SUBDIRECTORY); | |
| } | |
| done_with_mbr: | |
| // restore the registry key for Windows clock in UTC | |
| IFFALSE_PRINTERROR(ResetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_UTC_TIME_PATH, REGKEY_UTC_TIME), "Error on restoring Windows clock to UTC."); | |
| // restore Fast Start (aka Hiberboot) | |
| IFFALSE_PRINTERROR(ResetEndlessRegistryKey(HKEY_LOCAL_MACHINE, REGKEY_FASTBOOT_PATH, REGKEY_FASTBOOT), "Error on restoring fastboot."); | |
| // remove uninstall entry from registry | |
| do { | |
| CRegKey uninstallKey; | |
| LSTATUS result = uninstallKey.Open(HKEY_LOCAL_MACHINE, REGKEY_WIN_UNINSTALL, KEY_ALL_ACCESS); | |
| IFFALSE_CONTINUE(result == ERROR_SUCCESS, "Error opening uninstall key"); | |
| result = uninstallKey.DeleteSubKey(REGKEY_ENDLESS_OS); | |
| IFFALSE_CONTINUE(result == ERROR_SUCCESS, "Error on DeleteSubKey"); | |
| } while (FALSE); | |
| // remove <SYSDRIVE>:\Endless | |
| IFFALSE_PRINTERROR(ChangeAccessPermissions(endlessFilesPath, false), "Error on granting Endless files permissions."); | |
| uprintf("exePath=%ls, endless=%ls", exePath, endlessFilesPath); | |
| DelayDeleteFolder(endlessFilesPath); | |
| // set success message and icon | |
| popupMsgId = MSG_362; | |
| popupStyle = MB_OK | MB_ICONINFORMATION; | |
| retResult = TRUE; | |
| error: | |
| safe_closehandle(hPhysical); | |
| if (espMountLetter != NULL) AltUnmountVolume(espMountLetter); | |
| AfxMessageBox(UTF8ToCString(lmprintf(popupMsgId)), popupStyle); | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::ResetEndlessRegistryKey(HKEY parentKey, const CString &keyPath, const CString &keyName) | |
| { | |
| BOOL retResult = FALSE; | |
| CRegKey registryKey; | |
| LSTATUS result; | |
| LONG queryResult; | |
| DWORD keyType; | |
| ULONG dataSize = 0; | |
| uprintf("ResetEndlessRegistryKey called with path '%ls' and key '%ls'", keyPath, keyName); | |
| result = registryKey.Open(parentKey, keyPath, KEY_ALL_ACCESS); | |
| IFFALSE_GOTO(result == ERROR_SUCCESS, "Error opening registry key.", done); | |
| queryResult = registryKey.QueryValue(CString(REGKEY_BACKUP_PREFIX) + keyName, &keyType, NULL, &dataSize); | |
| IFFALSE_GOTO(queryResult == ERROR_SUCCESS, "Error on QueryValue. Backup value doesn't exist", done); | |
| switch (keyType) { | |
| case VT_R4: | |
| DWORD dwValue; | |
| result = registryKey.QueryDWORDValue(CString(REGKEY_BACKUP_PREFIX) + keyName, dwValue); | |
| IFFALSE_BREAK(result == ERROR_SUCCESS, "QueryDWORDValue failed for backup value"); | |
| result = registryKey.DeleteValue(CString(REGKEY_BACKUP_PREFIX) + keyName); | |
| IFFALSE_PRINTERROR(result == ERROR_SUCCESS, "Error on deleting backup value."); | |
| if (dwValue == REGKEY_DWORD_MISSING) { | |
| result = registryKey.DeleteValue(keyName); | |
| IFFALSE_PRINTERROR(result == ERROR_SUCCESS, "Error on deleting Endless added value."); | |
| } else { | |
| result = registryKey.SetDWORDValue(keyName, dwValue); | |
| IFFALSE_PRINTERROR(result == ERROR_SUCCESS, "Error on setting key to original value."); | |
| } | |
| break; | |
| default: | |
| uprintf("ERROR: registry key type %d not handled", keyType); | |
| goto error; | |
| } | |
| done: | |
| retResult = TRUE; | |
| error: | |
| return retResult; | |
| } | |
| BOOL CEndlessUsbToolDlg::MountESPFromDrive(HANDLE hPhysical, const char **espMountLetter, const CString &systemDriveLetter) | |
| { | |
| FUNCTION_ENTER; | |
| BOOL retResult = FALSE; | |
| BYTE layout[4096] = { 0 }; | |
| PDRIVE_LAYOUT_INFORMATION_EX DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)(void*)layout; | |
| DWORD size; | |
| BOOL result; | |
| // get partition layout | |
| result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, layout, sizeof(layout), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk layout."); | |
| IFFALSE_GOTOERROR(DriveLayout->PartitionStyle == PARTITION_STYLE_GPT, "Unexpected partition type. Partition style is not GPT"); | |
| DWORD efiPartitionNumber = -1; | |
| PARTITION_INFORMATION_EX *partition = NULL; | |
| for (DWORD index = 0; index < DriveLayout->PartitionCount; index++) { | |
| partition = &(DriveLayout->PartitionEntry[index]); | |
| if (partition->Gpt.PartitionType == PARTITION_SYSTEM_GUID) { | |
| uprintf("Found ESP\r\nPartition %d:\r\n Type: %s\r\n Name: '%ls'\r\n ID: %s", | |
| index + 1, GuidToString(&partition->Gpt.PartitionType), partition->Gpt.Name, GuidToString(&partition->Gpt.PartitionId)); | |
| efiPartitionNumber = partition->PartitionNumber; | |
| break; | |
| } | |
| } | |
| IFFALSE_GOTOERROR(efiPartitionNumber != -1, "ESP not found."); | |
| // Fail if EFI partition number is bigger than we can fit in the | |
| // uin8_t that AltMountVolume receives as parameter for partition number | |
| IFFALSE_GOTOERROR(efiPartitionNumber <= 0xFF, "EFI partition number is bigger than 255."); | |
| *espMountLetter = AltMountVolume(ConvertUnicodeToUTF8(systemDriveLetter.Left(2)), (uint8_t)efiPartitionNumber); | |
| IFFALSE_GOTOERROR(*espMountLetter != NULL, "Error assigning a letter to the ESP."); | |
| retResult = TRUE; | |
| error: | |
| return retResult; | |
| } | |
| // A combination of 2 of the options found here: http://www.catch22.net/tuts/self-deleting-executables | |
| // This needs to be tested on all supported OS versions; I tested on Windows 8 and XP and it works | |
| void CEndlessUsbToolDlg::DelayDeleteFolder(const CString &folder) | |
| { | |
| wchar_t *shortPath = NULL, cmd[MAX_PATH], params[MAX_PATH]; | |
| int timeoutSeconds = 5; | |
| long result = GetShortPathName(folder, shortPath, 0); | |
| IFFALSE_GOTOERROR(result != 0, "Error on getting size needed for GetShortPathName"); | |
| shortPath = new wchar_t[result]; | |
| result = GetShortPathName(folder, shortPath, result); | |
| IFFALSE_RETURN(result != 0, "Error on GetShortPathName"); | |
| // cmd.exe /q /c "for /l %N in () do timeout 5 >> NUL & @rmdir /S /Q C:\endless >> NUL & if not exist C:\endless exit" | |
| swprintf_s(params, L"/q /c \"for /l %%N in () do timeout %d >> NUL & @rmdir /S /Q %ls >> NUL & if not exist %ls exit\"", timeoutSeconds, shortPath, shortPath); | |
| IFFALSE_GOTOERROR(GetEnvironmentVariableW(L"ComSpec", cmd, MAX_PATH) != 0, "Error on GetEnvironmentVariableW"); | |
| uprintf("Running '%ls' with '%ls'", cmd, params); | |
| INT execResult = (INT)ShellExecute(0, 0, cmd, params, GetSystemDrive(), SW_HIDE); | |
| uprintf("ShellExecute returned %d", execResult); | |
| IFFALSE_GOTOERROR(execResult > 32, "Error returned by ShellExecute"); | |
| error: | |
| if (shortPath != NULL) { | |
| delete [] shortPath; | |
| shortPath = NULL; | |
| } | |
| } | |
| bool CEndlessUsbToolDlg::IsUninstaller() | |
| { | |
| CStringW exePath = GetExePath(); | |
| return CSTRING_GET_LAST(exePath, '\\') == ENDLESS_UNINSTALLER_NAME; | |
| } | |
| bool CEndlessUsbToolDlg::ShouldUninstall() | |
| { | |
| return (PathFileExists(GetSystemDrive() + PATH_ENDLESS_SUBDIRECTORY + ENDLESS_IMG_FILE_NAME) | |
| || IsUninstaller()); | |
| } | |
| #define MIN_DATE_IMAGE_BOOT L"161020" | |
| // Image boot means support for booting from endless/endless.img | |
| // on dual boot (ntfs) and combined USB (exfat) | |
| bool CEndlessUsbToolDlg::HasImageBootSupport(const CString &version, const CString &date) | |
| { | |
| return version[0] >= '3' && date >= MIN_DATE_IMAGE_BOOT; | |
| } | |
| bool CEndlessUsbToolDlg::PackedImageAlreadyExists(const CString &filePath, ULONGLONG expectedSize, ULONGLONG expectedUnpackedSize, bool isInstaller) | |
| { | |
| bool exists = false; | |
| CFile file; | |
| bool isUnpackedImage = false; | |
| uprintf("Called with '%ls', expected size = [%I64u] and expected unpacked size = [%I64u]", filePath, expectedSize, expectedUnpackedSize); | |
| if(!isInstaller) { | |
| isUnpackedImage = RemoteMatchesUnpackedImg(filePath); | |
| } | |
| CString actualFile = isUnpackedImage ? GET_IMAGE_PATH(ENDLESS_IMG_FILE_NAME) : filePath; | |
| IFFALSE_GOTOERROR(PathFileExists(actualFile), "File doesn't exists"); | |
| ULONGLONG extractedSize = GetExtractedSize(actualFile, isInstaller); | |
| file.Open(actualFile, CFile::modeRead); | |
| uprintf("size=%I64u, extracted size=%I64u", file.GetLength(), extractedSize); | |
| if (isUnpackedImage) { | |
| IFFALSE_GOTOERROR(expectedUnpackedSize == file.GetLength(), "Extracted size doesn't match"); | |
| } else { | |
| IFFALSE_GOTOERROR(expectedUnpackedSize == extractedSize, "Extracted size doesn't match"); | |
| IFFALSE_GOTOERROR(expectedSize == file.GetLength(), "Extracted size doesn't match"); | |
| } | |
| exists = true; | |
| error: | |
| return exists; | |
| } | |
| #define SIGNATURE_FILE_SIZE 819 | |
| ULONGLONG CEndlessUsbToolDlg::GetActualDownloadSize(const RemoteImageEntry &r, bool fullSize) | |
| { | |
| ULONGLONG size = SIGNATURE_FILE_SIZE; // img.gz.asc | |
| CString localFile = GET_IMAGE_PATH(CSTRING_GET_LAST(r.urlFile, '/')); | |
| bool localFileExists = PackedImageAlreadyExists(localFile, r.compressedSize, r.extractedSize, false); | |
| size += !fullSize && localFileExists ? 0 : r.compressedSize; | |
| if (IsDualBootOrCombinedUsb()) { | |
| size += r.bootArchiveSize + SIGNATURE_FILE_SIZE * 2; // img.asc and boot.zip.asc | |
| } else if (m_selectedInstallMethod == InstallMethod_t::ReformatterUsb) { | |
| CString installerFile = GET_IMAGE_PATH(CSTRING_GET_LAST(m_installerImage.urlFile, '/')); | |
| bool installerExists = PackedImageAlreadyExists(installerFile, m_installerImage.compressedSize, m_installerImage.extractedSize, true); | |
| size += SIGNATURE_FILE_SIZE; // installer img.gz.asc | |
| size += !fullSize && installerExists ? 0 : m_installerImage.compressedSize; | |
| } | |
| return size; | |
| } | |
| bool CEndlessUsbToolDlg::GetSignatureForLocalFile(const CString &file, CString &signature) | |
| { | |
| pFileImageEntry_t localEntry = NULL; | |
| if (0 != m_imageFiles.Lookup(file, localEntry) && localEntry->isUnpackedImage) { | |
| signature = localEntry->unpackedImgSigPath; | |
| } else { | |
| uprintf("Could not find entry for local file '%ls'", file); | |
| signature = file + SIGNATURE_FILE_EXT; | |
| } | |
| return true; | |
| } | |
| bool CEndlessUsbToolDlg::RemoteMatchesUnpackedImg(const CString &remoteFilePath, CString *unpackedImgSig) | |
| { | |
| pFileImageEntry_t localEntry = NULL; | |
| if (m_imageFiles.Lookup(GET_IMAGE_PATH(ENDLESS_IMG_FILE_NAME), localEntry)) { | |
| if (CSTRING_GET_PATH(localEntry->unpackedImgSigPath, '.') == CSTRING_GET_PATH(remoteFilePath, '.')) { | |
| if (unpackedImgSig != NULL) *unpackedImgSig = localEntry->unpackedImgSigPath; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool CEndlessUsbToolDlg::IsDualBootOrCombinedUsb() | |
| { | |
| return m_selectedInstallMethod == InstallMethod_t::InstallDualBoot || m_selectedInstallMethod == InstallMethod_t::CombinedUsb; | |
| } | |
| void CEndlessUsbToolDlg::UpdateDualBootTexts() | |
| { | |
| if (ShouldUninstall()) { | |
| SetElementText(_T(ELEMENT_DUALBOOT_TITLE), UTF8ToBSTR(lmprintf(MSG_364))); | |
| SetElementText(_T(ELEMENT_DUALBOOT_DESCRIPTION), UTF8ToBSTR(lmprintf(MSG_365))); | |
| SetElementText(_T(ELEMENT_DUALBOOT_INSTALL_BUTTON), UTF8ToBSTR(lmprintf(MSG_366))); | |
| SetElementText(_T(ELEMENT_DUALBOOT_RECOMMENDATION), UTF8ToBSTR(lmprintf(MSG_367))); | |
| CallJavascript(_T(JS_ENABLE_BUTTON), CComVariant(HTML_BUTTON_ID(_T(ELEMENT_DUALBOOT_INSTALL_BUTTON))), CComVariant(TRUE)); | |
| } | |
| if (IsUninstaller()) { | |
| CallJavascript(_T(JS_SHOW_ELEMENT), CComVariant(ELEMENT_DUALBOOT_ADVANCED_TEXT), CComVariant(FALSE)); | |
| } | |
| } | |
| void CEndlessUsbToolDlg::QueryAndDoUninstall() | |
| { | |
| int selected = AfxMessageBox(UTF8ToCString(lmprintf(MSG_361)), MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2); | |
| if (selected != IDYES) | |
| return; | |
| SetSelectedInstallMethod(InstallMethod_t::UninstallDualBoot); | |
| Analytics::instance()->screenTracking(_T("UninstallPage")); | |
| ShowWindow(SW_HIDE); | |
| TrackEvent(_T("Started")); | |
| if (UninstallDualBoot(this)) { | |
| TrackEvent(_T("Completed")); | |
| } else { | |
| TrackEvent(_T("Failed"), ErrorCauseToStr(m_lastErrorCause)); | |
| Analytics::instance()->exceptionTracking(_T("UninstallError"), TRUE); | |
| } | |
| EndDialog(IDCLOSE); | |
| } | |
| bool CEndlessUsbToolDlg::IsEndlessMBR(FILE* fp, const CString &endlessPath) | |
| { | |
| CString mbrImgPath = endlessPath + BACKUP_MBR_IMG; | |
| bool retResult = false; | |
| unsigned char endlessMbrData[MBR_WINDOWS_NT_MAGIC], existingMbrData[MBR_WINDOWS_NT_MAGIC]; | |
| FILE *mbrFile = NULL; | |
| memset(endlessMbrData, 0, MBR_WINDOWS_NT_MAGIC); | |
| memset(existingMbrData, 0, MBR_WINDOWS_NT_MAGIC); | |
| // load backup mbr data | |
| errno_t openRet = _wfopen_s(&mbrFile, mbrImgPath, L"rb"); | |
| if (openRet == ENOENT) { | |
| uprintf("%ls missing; assuming the Endless OS GRUB MBR is installed", mbrImgPath); | |
| return TRUE; | |
| } else { | |
| IFFALSE_GOTOERROR(0 == openRet, "Error opening mbr.img file"); | |
| } | |
| fread(endlessMbrData, 1, MBR_WINDOWS_NT_MAGIC, mbrFile); | |
| safe_closefile(mbrFile); | |
| // load current mbr data | |
| IFFALSE_GOTOERROR(read_data(fp, 0x0, existingMbrData, MBR_WINDOWS_NT_MAGIC), "Error reading current MBR"); | |
| retResult = (0 == memcmp(existingMbrData, endlessMbrData, MBR_WINDOWS_NT_MAGIC)); | |
| error: | |
| return retResult; | |
| } | |
| bool CEndlessUsbToolDlg::RestoreOriginalBoottrack(const CString &endlessPath, HANDLE hPhysical, FILE *fp) | |
| { | |
| bool retResult = false; | |
| DWORD size; | |
| BYTE geometry[256] = { 0 }; | |
| PDISK_GEOMETRY_EX DiskGeometry = (PDISK_GEOMETRY_EX)(void*)geometry; | |
| FILE *boottrackImgFile = NULL; | |
| unsigned char *boottrackData[MBR_WINDOWS_NT_MAGIC]; | |
| memset(boottrackData, 0, MBR_WINDOWS_NT_MAGIC); | |
| BOOL result = DeviceIoControl(hPhysical, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, geometry, sizeof(geometry), &size, NULL); | |
| IFFALSE_GOTOERROR(result != 0 && size > 0, "Error on querying disk geometry."); | |
| set_bytes_per_sector(SelectedDrive.Geometry.BytesPerSector); | |
| IFFALSE_GOTOERROR(0 == _wfopen_s(&boottrackImgFile, endlessPath + BACKUP_BOOTTRACK_IMG, L"rb"), "Error opening boottrack.img file"); | |
| IFFALSE_GOTOERROR(fread(boottrackData, 1, MBR_WINDOWS_NT_MAGIC, boottrackImgFile), "Error reading from boottrack.img"); | |
| IFFALSE_GOTOERROR(write_data(fp, 0x0, boottrackData, MBR_WINDOWS_NT_MAGIC), "Error writing boottrack.img data to MBR"); | |
| retResult = true; | |
| error: | |
| safe_closefile(boottrackImgFile); | |
| return retResult; | |
| } | |
| CComBSTR CEndlessUsbToolDlg::GetDownloadString(const RemoteImageEntry &imageEntry) | |
| { | |
| ULONGLONG size = GetActualDownloadSize(imageEntry); | |
| CStringA sizeT = SizeToHumanReadable(size, FALSE, use_fake_units); | |
| ULONGLONG fullSize = GetActualDownloadSize(imageEntry, true); | |
| CStringA fullSizeT = SizeToHumanReadable(fullSize, FALSE, use_fake_units); | |
| return UTF8ToBSTR(size == fullSize ? lmprintf(MSG_369, fullSizeT) : lmprintf(MSG_315, sizeT, fullSizeT)); | |
| } |