1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00

[net] Check for application update (part 5)

* Add dialog for new release notification
* Do not check for updates during format or ISO ops
* Add RTF support for parser and security improvements
* Also improve init and exit of progress dialog
This commit is contained in:
Pete Batard 2012-12-04 01:47:45 +00:00
parent eed0b78ff0
commit 4251e78fcd
9 changed files with 196 additions and 90 deletions

View file

@ -417,8 +417,7 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan)
SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE));
SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0);
} }
ShowWindow(hISOProgressDlg, SW_SHOW); SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0);
UpdateWindow(hISOProgressDlg);
/* First try to open as UDF - fallback to ISO if it failed */ /* First try to open as UDF - fallback to ISO if it failed */
p_udf = udf_open(src_iso); p_udf = udf_open(src_iso);
@ -514,7 +513,7 @@ out:
if (fd != NULL) if (fd != NULL)
fclose(fd); fclose(fd);
} }
ShowWindow(hISOProgressDlg, SW_HIDE); SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0);
if (p_iso != NULL) if (p_iso != NULL)
iso9660_close(p_iso); iso9660_close(p_iso);
if (p_udf != NULL) if (p_udf != NULL)

View file

@ -85,7 +85,7 @@ const char* additional_copyrights =
"All other references can be found in the source.\\line\n}"; "All other references can be found in the source.\\line\n}";
const char* update_policy = const char* update_policy =
"{\\rtf1\\ansi{\\fonttbl{\\f0\\fnil\\fcharset0 Tahoma;}{\\f1\\fnil\\fcharset2 Symbol;}}\n" "{\\rtf1\\ansi{\\fonttbl{\\f0\\fnil\\fcharset0 Microsoft Sans Serif;}{\\f1\\fnil\\fcharset2 Symbol;}}\n"
"\\fs16\\b Update Policy\\b0\\line\\line\n" "\\fs16\\b Update Policy\\b0\\line\\line\n"
"If you choose to allow update checks, you agree that the following information may be collected on our server(s):\\par\n" "If you choose to allow update checks, you agree that the following information may be collected on our server(s):\\par\n"
"\\pard{\\pntext\\f1\\'B7\\tab}{\\*\\pn\\pnlvlblt\\pnf2\\pnindent0{\\pntxtb\\'B7}}\\fi-150\\li220 Your Operating System's architecture and version\\par\n" "\\pard{\\pntext\\f1\\'B7\\tab}{\\*\\pn\\pnlvlblt\\pnf2\\pnindent0{\\pntxtb\\'B7}}\\fi-150\\li220 Your Operating System's architecture and version\\par\n"

View file

@ -252,13 +252,12 @@ BOOL DownloadFile(const char* url, const char* file)
progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE); progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE);
SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE));
SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0);
ShowWindow(hISOProgressDlg, SW_SHOW); SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0);
UpdateWindow(hISOProgressDlg);
PrintStatus(0, FALSE, "Downloading %s: Connecting...\n", file); PrintStatus(0, FALSE, "Downloading %s: Connecting...\n", file);
uprintf("Downloading %s from %s\n", file, url); uprintf("Downloading %s from %s\n", file, url);
if (!InternetCrackUrlA(url, safe_strlen(url), 0, &UrlParts)) { if (!InternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts)) {
uprintf("Unable to decode URL: %s\n", WindowsErrorString()); uprintf("Unable to decode URL: %s\n", WindowsErrorString());
goto out; goto out;
} }
@ -348,7 +347,7 @@ BOOL DownloadFile(const char* url, const char* file)
} }
out: out:
ShowWindow(hISOProgressDlg, SW_HIDE); SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0);
if (fd != NULL) fclose(fd); if (fd != NULL) fclose(fd);
if (!r) { if (!r) {
_unlink(file); _unlink(file);
@ -383,15 +382,17 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param)
BOOL is_x64 = FALSE, (__stdcall *pIsWow64Process)(HANDLE, PBOOL) = NULL; BOOL is_x64 = FALSE, (__stdcall *pIsWow64Process)(HANDLE, PBOOL) = NULL;
// Wait a while before checking for updates // Wait a while before checking for updates
// TODO: start the thread in sleep mode and only wake it on user inactivity? // TODO: Also check on inactivity
// Sleep(15000); do {
Sleep(10000);
} while (iso_op_in_progress || format_op_in_progress);
// verbose = ReadRegistryKey32(REGKEY_VERBOSE_UPDATES); // TODO: reenable this
// TODO: reenable this // verbose = ReadRegistryKey32(REGKEY_VERBOSE_UPDATES);
// if (GetRegistryKeyBool(REGKEY_DISABLE_UPDATES)) { if ((ReadRegistryKey32(REGKEY_UPDATE_INTERVAL) == -1)) {
// vuprintf("Check for updates disabled, as per registry settings.\n"); vuprintf("Check for updates disabled, as per registry settings.\n");
// return FALSE; goto out;
// } }
reg_time = ReadRegistryKey64(REGKEY_LAST_UPDATE); reg_time = ReadRegistryKey64(REGKEY_LAST_UPDATE);
update_interval = (int64_t)ReadRegistryKey32(REGKEY_UPDATE_INTERVAL); update_interval = (int64_t)ReadRegistryKey32(REGKEY_UPDATE_INTERVAL);
if (update_interval == 0) { if (update_interval == 0) {
@ -405,14 +406,14 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param)
vvuprintf("Local time: %" PRId64 "\n", local_time); vvuprintf("Local time: %" PRId64 "\n", local_time);
if (local_time < reg_time + update_interval) { if (local_time < reg_time + update_interval) {
vuprintf("Next update check in %" PRId64 " seconds.\n", reg_time + update_interval - local_time); vuprintf("Next update check in %" PRId64 " seconds.\n", reg_time + update_interval - local_time);
return FALSE; goto out;
} }
PrintStatus(3000, FALSE, "Checking for " APPLICATION_NAME " updates...\n"); PrintStatus(3000, FALSE, "Checking for " APPLICATION_NAME " updates...\n");
if (!GetVersionExA(&os_version)) { if (!GetVersionExA(&os_version)) {
vuprintf("Could not read Windows version - Check for updates cancelled.\n"); vuprintf("Could not read Windows version - Check for updates cancelled.\n");
return FALSE; goto out;
} }
// Detect if the OS is 64 bit, without relying on external libs // Detect if the OS is 64 bit, without relying on external libs
@ -427,7 +428,7 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param)
is_x64 = TRUE; is_x64 = TRUE;
} }
if ((!InternetCrackUrlA(server_url, safe_strlen(server_url), 0, &UrlParts)) || (!InternetGetConnectedState(&dwFlags, 0))) if ((!InternetCrackUrlA(server_url, (DWORD)safe_strlen(server_url), 0, &UrlParts)) || (!InternetGetConnectedState(&dwFlags, 0)))
goto out; goto out;
_snprintf(agent, ARRAYSIZE(agent), APPLICATION_NAME "/%d.%d.%d.%d", rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]); _snprintf(agent, ARRAYSIZE(agent), APPLICATION_NAME "/%d.%d.%d.%d", rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]);
@ -447,7 +448,7 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param)
safe_sprintf(urlpath, sizeof(urlpath), "%s_%s_%d.%d.ver", APPLICATION_NAME, safe_sprintf(urlpath, sizeof(urlpath), "%s_%s_%d.%d.ver", APPLICATION_NAME,
archname[is_x64?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion); archname[is_x64?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion);
vuprintf("Base update check: %s\n", urlpath); vuprintf("Base update check: %s\n", urlpath);
for (i=0, j=safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) { for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) {
if ((urlpath[j] == '.') || (urlpath[j] == '_')) { if ((urlpath[j] == '.') || (urlpath[j] == '_')) {
verpos[i++] = j; verpos[i++] = j;
} }
@ -501,10 +502,10 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param)
WriteRegistryKey64(REGKEY_LAST_UPDATE, server_time); WriteRegistryKey64(REGKEY_LAST_UPDATE, server_time);
// Might as well let the user know // Might as well let the user know
if (local_time > server_time + 600) { if (local_time > server_time + 600) {
uprintf("Your local clock seems more than 10 minutes early - You probably want to fix that...\n"); uprintf("Your local clock appears more than 10 minutes early - You ought to fix that...\n");
} }
if (local_time < server_time - 600) { if (local_time < server_time - 600) {
uprintf("Your local clock seems more than 10 minutes late - you probably want to fix that...\n"); uprintf("Your local clock appears more than 10 minutes late - you ought to fix that...\n");
} }
dwSize = sizeof(dwTotalSize); dwSize = sizeof(dwTotalSize);
@ -533,11 +534,9 @@ out:
BOOL CheckForUpdates(void) BOOL CheckForUpdates(void)
{ {
// TODO: check registry for disabled update
if (CreateThread(NULL, 0, CheckForUpdatesThread, NULL, 0, 0) == NULL) { if (CreateThread(NULL, 0, CheckForUpdatesThread, NULL, 0, 0) == NULL) {
uprintf("Unable to start check for updates thread"); uprintf("Unable to start check for updates thread");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_START_THREAD); return FALSE;
} }
return TRUE; return TRUE;
} }

View file

@ -40,7 +40,7 @@ typedef struct {
char* platform; // target platform ("windows", "linux", etc.) char* platform; // target platform ("windows", "linux", etc.)
char* platform_arch; // "x86", "x64", "arm" char* platform_arch; // "x86", "x64", "arm"
char* platform_min; // minimum platform version required char* platform_min; // minimum platform version required
char* download_url[2]; char* download_url;
char* release_notes; char* release_notes;
} rufus_update; } rufus_update;
@ -51,16 +51,12 @@ typedef struct {
static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline)
{ {
const wchar_t wspace[] = L" \t"; // The only whitespaces we recognize as such const wchar_t wspace[] = L" \t"; // The only whitespaces we recognize as such
const wchar_t weol[] = L"\r\n";
size_t i, r; size_t i, r;
BOOLEAN quoteth = FALSE; BOOLEAN quoteth = FALSE;
if ((wtoken == NULL) || (wline == NULL) || (wline[0] == 0)) if ((wtoken == NULL) || (wline == NULL) || (wline[0] == 0))
return NULL; return NULL;
// Eliminate trailing EOL characters
wline[wcscspn(wline, weol)] = 0;
i = 0; i = 0;
// Skip leading spaces // Skip leading spaces
@ -98,6 +94,10 @@ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline)
i++; i++;
wline[i] = 0; wline[i] = 0;
// Eliminate trailing EOL characters
while ((i>=r) && ((wline[i] == L'\r') || (wline[i] == L'\n')))
wline[i--] = 0;
return (wline[r] == 0)?NULL:&wline[r]; return (wline[r] == 0)?NULL:&wline[r];
} }
@ -150,7 +150,7 @@ out:
// The returned string is UTF-8 and MUST be freed by the caller // The returned string is UTF-8 and MUST be freed by the caller
char* get_token_data_buffer(const char* token, unsigned int n, const char* buffer, size_t buffer_size) char* get_token_data_buffer(const char* token, unsigned int n, const char* buffer, size_t buffer_size)
{ {
unsigned int j; unsigned int j, curly_count;
wchar_t *wtoken = NULL, *wdata = NULL, *wbuffer = NULL, *wline = NULL; wchar_t *wtoken = NULL, *wdata = NULL, *wbuffer = NULL, *wline = NULL;
size_t i; size_t i;
BOOL done = FALSE; BOOL done = FALSE;
@ -169,11 +169,14 @@ char* get_token_data_buffer(const char* token, unsigned int n, const char* buffe
if ((wbuffer == NULL) || (wtoken == NULL)) if ((wbuffer == NULL) || (wtoken == NULL))
goto out; goto out;
// Process individual lines // Process individual lines (or multiple lines when between {}, for RTF)
for (i=0,j=0,done=FALSE; (j!=n)&&(!done); ) { for (i=0,j=0,done=FALSE; (j!=n)&&(!done); ) {
wline = &wbuffer[i]; wline = &wbuffer[i];
for(;(wbuffer[i]!=L'\n')&&(wbuffer[i]!=L'\r')&&(wbuffer[i]!=0);i++); for(curly_count=0;((curly_count>0)||((wbuffer[i]!=L'\n')&&(wbuffer[i]!=L'\r')))&&(wbuffer[i]!=0);i++) {
if (wbuffer[i] == L'{') curly_count++;
if (wbuffer[i] == L'}') curly_count--;
}
if (wbuffer[i]==0) { if (wbuffer[i]==0) {
done = TRUE; done = TRUE;
} else { } else {
@ -210,22 +213,29 @@ static __inline char* get_sanitized_token_data_buffer(const char* token, unsigne
// Parse an update data file and populates a rufus_update structure. // Parse an update data file and populates a rufus_update structure.
// NB: since this is remote data, and we're running elevated, it *IS* considered // NB: since this is remote data, and we're running elevated, it *IS* considered
// potentially malicious, even if it comes from a supposedly trusted server. // potentially malicious, even if it comes from a supposedly trusted server.
// len should be the size of the buffer - 1, for the zero terminator // len should be the size of the buffer, including the zero terminator
extern INT_PTR NewVersionDialog(const char* notes, const char* url);
void parse_update(char* buf, size_t len) void parse_update(char* buf, size_t len)
{ {
size_t i; size_t i;
char *data = NULL, *token; char *data = NULL, *token;
char allowed_chars[] = " \t\r\nabcdefghijklmnopqrstuvwxyz" char allowed_rtf_chars[] = "abcdefghijklmnopqrstuvwxyz|~-_:*'";
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&*()-_+=<>(){}[].,:;#@'/?|~"; char allowed_std_chars[] = "\r\n ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&+=<>(){}[].,;#@/?";
rufus_update update; rufus_update update;
if ((buf == NULL) || (len < 2) || (len > 65536) || (buf[len-1] != 0)) // strchr includes the NUL terminator in the search, so take care of backslash before NUL
if ((buf == NULL) || (len < 2) || (len > 65536) || (buf[len-1] != 0) || (buf[len-2] == '\\'))
return; return;
// Sanitize the data - Of course not a silver bullet, but it helps // Sanitize the data - Not a silver bullet, but it helps
len = safe_strlen(buf)+1; // Someone may be inserting NULs
for (i=0; i<len-1; i++) { for (i=0; i<len-1; i++) {
// Do not sanitize \n yet // Check for valid RTF sequences as well as allowed chars if not RTF
// NB: we have a zero terminator, so we can afford a +1 without overflow if (buf[i] == '\\') {
if ((strchr(allowed_chars, buf[i]) == NULL) && (buf[i] != '\\') && (buf[i+1] != 'n')) { // NB: we have a zero terminator, so we can afford a +1 without overflow
if (strchr(allowed_rtf_chars, buf[i+1]) == NULL) {
buf[i] = ' ';
}
} else if ((strchr(allowed_rtf_chars, buf[i]) == NULL) && (strchr(allowed_std_chars, buf[i]) == NULL)) {
buf[i] = ' '; buf[i] = ' ';
} }
} }
@ -237,24 +247,25 @@ void parse_update(char* buf, size_t len)
} }
safe_free(data); safe_free(data);
} }
// TODO: use X-Macros?
update.type = get_sanitized_token_data_buffer("type", 1, buf, len); update.type = get_sanitized_token_data_buffer("type", 1, buf, len);
update.platform = get_sanitized_token_data_buffer("platform", 1, buf, len); update.platform = get_sanitized_token_data_buffer("platform", 1, buf, len);
update.platform_arch = get_sanitized_token_data_buffer("platform_arch", 1, buf, len); update.platform_arch = get_sanitized_token_data_buffer("platform_arch", 1, buf, len);
update.platform_min = get_sanitized_token_data_buffer("platform_min", 1, buf, len); update.platform_min = get_sanitized_token_data_buffer("platform_min", 1, buf, len);
for (i=0; i<ARRAYSIZE(update.download_url); i++) { update.download_url = get_sanitized_token_data_buffer("download_url", 1, buf, len);
update.download_url[i] = get_sanitized_token_data_buffer("download_url", (unsigned int)i+1, buf, len);
}
update.release_notes = get_sanitized_token_data_buffer("release_notes", 1, buf, len); update.release_notes = get_sanitized_token_data_buffer("release_notes", 1, buf, len);
uprintf("UPDATE DATA:\n"); uprintf("UPDATE DATA:\n");
uprintf(" version: %d.%d.%d.%d\n", update.version[0], update.version[1], update.version[2], update.version[3]); uprintf(" version: %d.%d.%d.%d\n", update.version[0], update.version[1], update.version[2], update.version[3]);
uprintf(" platform: %s\r\n platform_arch: %s\r\n platform_min: %s\n", update.platform, update.platform_arch, update.platform_min); uprintf(" platform: %s\r\n platform_arch: %s\r\n platform_min: %s\n", update.platform, update.platform_arch, update.platform_min);
for (i=0; i<ARRAYSIZE(update.download_url); i++) { uprintf(" url: %s\n", update.download_url);
uprintf(" url%d: %s\n", i+1, update.download_url[i]);
}
uprintf("RELEASE NOTES:\r\n%s\n", update.release_notes); uprintf("RELEASE NOTES:\r\n%s\n", update.release_notes);
// User may have started formatting while we were checking
while (iso_op_in_progress || format_op_in_progress) {
Sleep(3000);
}
NewVersionDialog(update.release_notes, update.download_url);
// TODO: free all these strings! // TODO: free all these strings!
} }

View file

@ -13,6 +13,7 @@
#define IDI_UP 109 #define IDI_UP 109
#define IDI_DOWN 110 #define IDI_DOWN 110
#define IDD_UPDATE_POLICY 111 #define IDD_UPDATE_POLICY 111
#define IDD_NEW_VERSION 112
#define IDR_BR_MBR_BIN 200 #define IDR_BR_MBR_BIN 200
#define IDR_FD_COMMAND_COM 300 #define IDR_FD_COMMAND_COM 300
#define IDR_FD_KERNEL_SYS 301 #define IDR_FD_KERNEL_SYS 301
@ -71,6 +72,7 @@
#define IDC_ABOUT_ICON 1031 #define IDC_ABOUT_ICON 1031
#define IDC_ABOUT_UPDATES 1032 #define IDC_ABOUT_UPDATES 1032
#define IDC_ABOUT_COPYRIGHTS 1033 #define IDC_ABOUT_COPYRIGHTS 1033
#define IDC_ABOUT_BLURB 1034
#define IDC_LICENSE_TEXT 1036 #define IDC_LICENSE_TEXT 1036
#define IDC_NOTIFICATION_ICON 1040 #define IDC_NOTIFICATION_ICON 1040
#define IDC_NOTIFICATION_TEXT 1041 #define IDC_NOTIFICATION_TEXT 1041
@ -85,16 +87,17 @@
#define IDC_POLICY 1061 #define IDC_POLICY 1061
#define IDC_UPDATE_FREQUENCY 1062 #define IDC_UPDATE_FREQUENCY 1062
#define IDC_INCLUDE_BETAS 1063 #define IDC_INCLUDE_BETAS 1063
#define IDC_ABOUT_BLURB 1064 #define IDC_RELEASE_NOTES 1064
#define IDC_DOWNLOAD 1065
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1 #define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 112 #define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1065 #define _APS_NEXT_CONTROL_VALUE 1066
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif
#endif #endif

View file

@ -82,7 +82,7 @@ struct {
} bi_iso = {0}, bi_up = {0}, bi_down = {0}; // BUTTON_IMAGELIST } bi_iso = {0}, bi_up = {0}, bi_down = {0}; // BUTTON_IMAGELIST
static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" }; static const char* FileSystemLabel[FS_MAX] = { "FAT", "FAT32", "NTFS", "exFAT" };
// Don't ask me - just following the MS standard here // Don't ask me - just following the MS "standard" here
static const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes", static const char* ClusterSizeLabel[] = { "512 bytes", "1024 bytes","2048 bytes","4096 bytes","8192 bytes",
"16 kilobytes", "32 kilobytes", "64 kilobytes", "128 kilobytes", "256 kilobytes", "512 kilobytes", "16 kilobytes", "32 kilobytes", "64 kilobytes", "128 kilobytes", "256 kilobytes", "512 kilobytes",
"1024 kilobytes","2048 kilobytes","4096 kilobytes","8192 kilobytes","16 megabytes","32 megabytes" }; "1024 kilobytes","2048 kilobytes","4096 kilobytes","8192 kilobytes","16 megabytes","32 megabytes" };
@ -104,6 +104,7 @@ int default_fs;
HWND hDeviceList, hCapacity, hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog = NULL; HWND hDeviceList, hCapacity, hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog = NULL;
HWND hISOProgressDlg = NULL, hLogDlg = NULL, hISOProgressBar, hISOFileName, hDiskID; HWND hISOProgressDlg = NULL, hLogDlg = NULL, hISOProgressBar, hISOFileName, hDiskID;
BOOL use_own_vesamenu = FALSE, detect_fakes = TRUE, mbr_selected_by_user = FALSE; BOOL use_own_vesamenu = FALSE, detect_fakes = TRUE, mbr_selected_by_user = FALSE;
BOOL iso_op_in_progress = FALSE, format_op_in_progress = FALSE;
int rufus_version[4]; int rufus_version[4];
extern char szStatusMessage[256]; extern char szStatusMessage[256];
@ -1192,20 +1193,26 @@ static void CALLBACK BlockingTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD
} }
} }
/* Callback for the modeless ISO extraction progress */ /* Callback for the modeless ISO extraction progress, and other progress dialogs */
BOOL CALLBACK ISOProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) BOOL CALLBACK ISOProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{ {
switch (message) { switch (message) {
case WM_INITDIALOG: case WM_INITDIALOG:
CenterDialog(hDlg);
hISOProgressBar = GetDlgItem(hDlg, IDC_ISO_PROGRESS); hISOProgressBar = GetDlgItem(hDlg, IDC_ISO_PROGRESS);
hISOFileName = GetDlgItem(hDlg, IDC_ISO_FILENAME); hISOFileName = GetDlgItem(hDlg, IDC_ISO_FILENAME);
// Use maximum granularity for the progress bar // Use maximum granularity for the progress bar
SendMessage(hISOProgressBar, PBM_SETRANGE, 0, MAX_PROGRESS<<16); SendMessage(hISOProgressBar, PBM_SETRANGE, 0, MAX_PROGRESS<<16);
return TRUE; return TRUE;
case UM_ISO_INIT:
iso_op_in_progress = TRUE;
CenterDialog(hDlg);
ShowWindow(hDlg, SW_SHOW);
UpdateWindow(hDlg);
return TRUE;
case UM_ISO_EXIT: case UM_ISO_EXIT:
DestroyWindow(hDlg); // Just hide and recentrer the dialog
hISOProgressDlg = NULL; ShowWindow(hDlg, SW_HIDE);
iso_op_in_progress = FALSE;
return TRUE; return TRUE;
case WM_COMMAND: case WM_COMMAND:
switch (LOWORD(wParam)) { switch (LOWORD(wParam)) {
@ -1521,14 +1528,12 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
{ {
DRAWITEMSTRUCT* pDI; DRAWITEMSTRUCT* pDI;
POINT Point; POINT Point;
BOOL testme;
RECT DialogRect, DesktopRect; RECT DialogRect, DesktopRect;
int nDeviceIndex, fs, i, nWidth, nHeight; int nDeviceIndex, fs, i, nWidth, nHeight;
static DWORD DeviceNum = 0; static DWORD DeviceNum = 0;
wchar_t wtmp[128], wstr[MAX_PATH]; wchar_t wtmp[128], wstr[MAX_PATH];
static UINT uDOSChecked = BST_CHECKED, uQFChecked; static UINT uDOSChecked = BST_CHECKED, uQFChecked;
static BOOL first_log_display = TRUE, user_changed_label = FALSE; static BOOL first_log_display = TRUE, user_changed_label = FALSE;
notification_info more_info = { IDD_UPDATE_POLICY, UpdateCallback };
switch (message) { switch (message) {
@ -1547,6 +1552,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
hLogDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_LOG), hDlg, (DLGPROC)LogProc); hLogDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_LOG), hDlg, (DLGPROC)LogProc);
InitDialog(hDlg); InitDialog(hDlg);
GetUSBDevices(0); GetUSBDevices(0);
CheckForUpdates();
PostMessage(hMainDialog, UM_ISO_CREATE, 0, 0);
return (INT_PTR)TRUE; return (INT_PTR)TRUE;
// The things one must do to get an ellipsis on the status bar... // The things one must do to get an ellipsis on the status bar...
@ -1769,14 +1776,6 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
selection_default = DT_ISO; selection_default = DT_ISO;
CreateTooltip(hSelectISO, iso_path, -1); CreateTooltip(hSelectISO, iso_path, -1);
FormatStatus = 0; FormatStatus = 0;
// You'd think that Windows would let you instantiate a modeless dialog wherever
// but you'd be wrong. It must be done in the main callback!
if (!IsWindow(hISOProgressDlg)) {
hISOProgressDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_ISO_EXTRACT),
hDlg, (DLGPROC)ISOProc);
// The window is not visible by default but takes focus => restore it
SetFocus(hDlg);
}
if (CreateThread(NULL, 0, ISOScanThread, NULL, 0, 0) == NULL) { if (CreateThread(NULL, 0, ISOScanThread, NULL, 0, 0) == NULL) {
uprintf("Unable to start ISO scanning thread"); uprintf("Unable to start ISO scanning thread");
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_START_THREAD); FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_START_THREAD);
@ -1791,6 +1790,7 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
return (INT_PTR)TRUE; return (INT_PTR)TRUE;
} }
FormatStatus = 0; FormatStatus = 0;
format_op_in_progress = TRUE;
// Reset all progress bars // Reset all progress bars
SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0); SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0);
SetTaskbarProgressState(TASKBAR_NORMAL); SetTaskbarProgressState(TASKBAR_NORMAL);
@ -1838,12 +1838,6 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex); DeviceNum = (DWORD)ComboBox_GetItemData(hDeviceList, nDeviceIndex);
FormatStatus = 0; FormatStatus = 0;
InitProgress(); InitProgress();
if (!IsWindow(hISOProgressDlg)) {
hISOProgressDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_ISO_EXTRACT),
hDlg, (DLGPROC)ISOProc);
// The window is not visible by default but takes focus => restore it
SetFocus(hDlg);
}
format_thid = CreateThread(NULL, 0, FormatThread, (LPVOID)(uintptr_t)DeviceNum, 0, NULL); format_thid = CreateThread(NULL, 0, FormatThread, (LPVOID)(uintptr_t)DeviceNum, 0, NULL);
if (format_thid == NULL) { if (format_thid == NULL) {
uprintf("Unable to start formatting thread"); uprintf("Unable to start formatting thread");
@ -1857,6 +1851,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
SetTimer(hMainDialog, TID_APP_TIMER, 1000, ClockTimer); SetTimer(hMainDialog, TID_APP_TIMER, 1000, ClockTimer);
} }
} }
if (format_thid == NULL)
format_op_in_progress = FALSE;
break; break;
default: default:
return (INT_PTR)FALSE; return (INT_PTR)FALSE;
@ -1870,6 +1866,17 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
PostQuitMessage(0); PostQuitMessage(0);
break; break;
case UM_ISO_CREATE:
// You'd think that Windows would let you instantiate a modeless dialog wherever
// but you'd be wrong. It must be done in the main callback, hence the custom message.
if (!IsWindow(hISOProgressDlg)) {
hISOProgressDlg = CreateDialogA(hMainInstance, MAKEINTRESOURCEA(IDD_ISO_EXTRACT),
hDlg, (DLGPROC)ISOProc);
// The window is not visible by default but takes focus => restore it
SetFocus(hDlg);
}
return (INT_PTR)TRUE;
case UM_FORMAT_COMPLETED: case UM_FORMAT_COMPLETED:
format_thid = NULL; format_thid = NULL;
// Stop the timer // Stop the timer
@ -1900,6 +1907,8 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA
Notification(MSG_ERROR, NULL, "Error", "Error: %s.%s", StrError(FormatStatus), Notification(MSG_ERROR, NULL, "Error", "Error: %s.%s", StrError(FormatStatus),
(strchr(StrError(FormatStatus), '\n') != NULL)?"":"\nFor more information, please check the log."); (strchr(StrError(FormatStatus), '\n') != NULL)?"":"\nFor more information, please check the log.");
} }
FormatStatus = 0;
format_op_in_progress = FALSE;
return (INT_PTR)TRUE; return (INT_PTR)TRUE;
} }
return (INT_PTR)FALSE; return (INT_PTR)FALSE;

View file

@ -85,6 +85,9 @@ extern void _uprintf(const char *format, ...);
/* Custom Windows messages */ /* Custom Windows messages */
enum user_message_type { enum user_message_type {
UM_FORMAT_COMPLETED = WM_APP, UM_FORMAT_COMPLETED = WM_APP,
// TODO: relabel "ISO" to a more generic "progress"
UM_ISO_CREATE,
UM_ISO_INIT,
UM_ISO_EXIT UM_ISO_EXIT
}; };
@ -202,12 +205,12 @@ extern HWND hMainDialog, hLogDlg, hStatus, hDeviceList, hCapacity;
extern HWND hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog; extern HWND hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog;
extern HWND hISOProgressDlg, hISOProgressBar, hISOFileName, hDiskID; extern HWND hISOProgressDlg, hISOProgressBar, hISOFileName, hDiskID;
extern float fScale; extern float fScale;
extern char szFolderPath[MAX_PATH]; extern char szFolderPath[MAX_PATH], app_dir[MAX_PATH];
extern char* iso_path; extern char* iso_path;
extern DWORD FormatStatus; extern DWORD FormatStatus;
extern RUFUS_DRIVE_INFO SelectedDrive; extern RUFUS_DRIVE_INFO SelectedDrive;
extern const int nb_steps[FS_MAX]; extern const int nb_steps[FS_MAX];
extern BOOL use_own_vesamenu, detect_fakes; extern BOOL use_own_vesamenu, detect_fakes, iso_op_in_progress, format_op_in_progress;
extern RUFUS_ISO_REPORT iso_report; extern RUFUS_ISO_REPORT iso_report;
extern int64_t iso_blocking_status; extern int64_t iso_blocking_status;
extern int rufus_version[4]; extern int rufus_version[4];

View file

@ -30,7 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL
IDD_DIALOG DIALOGEX 12, 12, 206, 316 IDD_DIALOG DIALOGEX 12, 12, 206, 316
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW EXSTYLE WS_EX_APPWINDOW
CAPTION "Rufus v1.2.1.201" CAPTION "Rufus v1.2.1.202"
FONT 8, "MS Shell Dlg", 400, 0, 0x1 FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN BEGIN
DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 DEFPUSHBUTTON "Start",IDC_START,94,278,50,14
@ -74,8 +74,8 @@ BEGIN
ICON IDI_ICON,IDC_ABOUT_ICON,11,8,20,20 ICON IDI_ICON,IDC_ABOUT_ICON,11,8,20,20
DEFPUSHBUTTON "OK",IDOK,231,181,50,14,WS_GROUP DEFPUSHBUTTON "OK",IDOK,231,181,50,14,WS_GROUP
PUSHBUTTON "License",IDC_ABOUT_LICENSE,46,181,50,14,WS_GROUP PUSHBUTTON "License",IDC_ABOUT_LICENSE,46,181,50,14,WS_GROUP
CONTROL "",IDC_ABOUT_COPYRIGHTS,"RichEdit20W",ES_MULTILINE | ES_READONLY | WS_VSCROLL,46,101,235,74,WS_EX_STATICEDGE CONTROL "",IDC_ABOUT_COPYRIGHTS,"RichEdit20W",WS_VSCROLL | 0x804,46,101,235,74,WS_EX_STATICEDGE
CONTROL "",IDC_ABOUT_BLURB,"RichEdit20W",ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_TABSTOP,46,7,235,93 CONTROL "",IDC_ABOUT_BLURB,"RichEdit20W",WS_TABSTOP | 0x884,46,7,235,93
PUSHBUTTON "Updates",IDC_ABOUT_UPDATES,100,181,50,14,NOT WS_VISIBLE | WS_GROUP PUSHBUTTON "Updates",IDC_ABOUT_UPDATES,100,181,50,14,NOT WS_VISIBLE | WS_GROUP
END END
@ -106,7 +106,7 @@ BEGIN
EDITTEXT IDC_LOG_EDIT,0,0,366,252,ES_MULTILINE | ES_READONLY | NOT WS_BORDER | WS_VSCROLL,WS_EX_STATICEDGE EDITTEXT IDC_LOG_EDIT,0,0,366,252,ES_MULTILINE | ES_READONLY | NOT WS_BORDER | WS_VSCROLL,WS_EX_STATICEDGE
PUSHBUTTON "Clear Log",IDC_LOG_CLEAR,198,259,50,14 PUSHBUTTON "Clear Log",IDC_LOG_CLEAR,198,259,50,14
PUSHBUTTON "Save Log",IDC_LOG_SAVE,253,259,50,14 PUSHBUTTON "Save Log",IDC_LOG_SAVE,253,259,50,14
PUSHBUTTON "Close Log",IDCANCEL,308,259,50,14 DEFPUSHBUTTON "Close Log",IDCANCEL,308,259,50,14
END END
IDD_NOTIFICATION DIALOGEX 0, 0, 263, 63 IDD_NOTIFICATION DIALOGEX 0, 0, 263, 63
@ -119,8 +119,8 @@ BEGIN
ICON 32516,IDC_NOTIFICATION_ICON,6,6,20,20 ICON 32516,IDC_NOTIFICATION_ICON,6,6,20,20
LTEXT "",IDC_NOTIFICATION_TEXT,35,10,219,20 LTEXT "",IDC_NOTIFICATION_TEXT,35,10,219,20
DEFPUSHBUTTON "No",IDNO,206,44,50,14 DEFPUSHBUTTON "No",IDNO,206,44,50,14
DEFPUSHBUTTON "More info...",IDC_MORE_INFO,8,44,50,14,NOT WS_VISIBLE PUSHBUTTON "More info...",IDC_MORE_INFO,8,44,50,14,NOT WS_VISIBLE
DEFPUSHBUTTON "Yes",IDYES,154,44,50,14,NOT WS_VISIBLE PUSHBUTTON "Yes",IDYES,154,44,50,14,NOT WS_VISIBLE
END END
IDD_UPDATE_POLICY DIALOGEX 0, 0, 287, 198 IDD_UPDATE_POLICY DIALOGEX 0, 0, 287, 198
@ -129,8 +129,8 @@ CAPTION "Update policy and settings"
FONT 8, "Microsoft Sans Serif", 400, 0, 0x0 FONT 8, "Microsoft Sans Serif", 400, 0, 0x0
BEGIN BEGIN
ICON IDI_ICON,IDC_ABOUT_ICON,11,8,21,20 ICON IDI_ICON,IDC_ABOUT_ICON,11,8,21,20
DEFPUSHBUTTON "Close",IDCLOSE,229,176,50,14,WS_GROUP DEFPUSHBUTTON "Close",IDCANCEL,229,176,50,14,WS_GROUP
CONTROL "",IDC_POLICY,"RichEdit20W",ES_MULTILINE | ES_READONLY | WS_VSCROLL,46,8,235,130,WS_EX_STATICEDGE CONTROL "",IDC_POLICY,"RichEdit20W",WS_VSCROLL | 0x804,46,8,235,130,WS_EX_STATICEDGE
COMBOBOX IDC_UPDATE_FREQUENCY,145,155,66,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_UPDATE_FREQUENCY,145,155,66,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Check for updates (at most):",IDC_STATIC,52,157,92,11 LTEXT "Check for updates (at most):",IDC_STATIC,52,157,92,11
LTEXT "Include beta versions:",IDC_STATIC,52,173,93,11 LTEXT "Include beta versions:",IDC_STATIC,52,173,93,11
@ -138,6 +138,16 @@ BEGIN
GROUPBOX "Settings",IDC_STATIC,46,145,173,45 GROUPBOX "Settings",IDC_STATIC,46,145,173,45
END END
IDD_NEW_VERSION DIALOGEX 0, 0, 287, 198
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "New version available"
FONT 8, "Microsoft Sans Serif", 400, 0, 0x0
BEGIN
PUSHBUTTON "Close",IDCANCEL,229,177,50,14,WS_GROUP
CONTROL "",IDC_RELEASE_NOTES,"RichEdit20W",ES_MULTILINE | ES_READONLY | WS_VSCROLL,4,4,279,167,WS_EX_STATICEDGE
DEFPUSHBUTTON "Download",IDC_DOWNLOAD,173,177,50,14,WS_GROUP
END
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -239,6 +249,10 @@ BEGIN
IDD_UPDATE_POLICY, DIALOG IDD_UPDATE_POLICY, DIALOG
BEGIN BEGIN
END END
IDD_NEW_VERSION, DIALOG
BEGIN
END
END END
#endif // APSTUDIO_INVOKED #endif // APSTUDIO_INVOKED
@ -249,8 +263,8 @@ END
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,2,1,201 FILEVERSION 1,2,1,202
PRODUCTVERSION 1,2,1,201 PRODUCTVERSION 1,2,1,202
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -267,13 +281,13 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)"
VALUE "FileDescription", "Rufus" VALUE "FileDescription", "Rufus"
VALUE "FileVersion", "1.2.1.201" VALUE "FileVersion", "1.2.1.202"
VALUE "InternalName", "Rufus" VALUE "InternalName", "Rufus"
VALUE "LegalCopyright", "(c) 2011-2012 Pete Batard (GPL v3)" VALUE "LegalCopyright", "(c) 2011-2012 Pete Batard (GPL v3)"
VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html"
VALUE "OriginalFilename", "rufus.exe" VALUE "OriginalFilename", "rufus.exe"
VALUE "ProductName", "Rufus" VALUE "ProductName", "Rufus"
VALUE "ProductVersion", "1.2.1.201" VALUE "ProductVersion", "1.2.1.202"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View file

@ -458,9 +458,9 @@ char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_des
INIT_VISTA_SHELL32; INIT_VISTA_SHELL32;
if (IS_VISTA_SHELL32_AVAILABLE) { if (IS_VISTA_SHELL32_AVAILABLE) {
// Setup the file extension filter table // Setup the file extension filter table
ext_filter = (char*)malloc(strlen(ext)+3); ext_filter = (char*)malloc(safe_strlen(ext)+3);
if (ext_filter != NULL) { if (ext_filter != NULL) {
safe_sprintf(ext_filter, strlen(ext)+3, "*.%s", ext); safe_sprintf(ext_filter, safe_strlen(ext)+3, "*.%s", ext);
filter_spec[0].pszSpec = utf8_to_wchar(ext_filter); filter_spec[0].pszSpec = utf8_to_wchar(ext_filter);
safe_free(ext_filter); safe_free(ext_filter);
filter_spec[0].pszName = utf8_to_wchar(ext_desc); filter_spec[0].pszName = utf8_to_wchar(ext_desc);
@ -536,7 +536,7 @@ fallback:
ofn.lpstrFile = selected_name; ofn.lpstrFile = selected_name;
ofn.nMaxFile = MAX_PATH; ofn.nMaxFile = MAX_PATH;
// Set the file extension filters // Set the file extension filters
ext_strlen = strlen(ext_desc) + 2*strlen(ext) + sizeof(" (*.)\0*.\0All Files (*.*)\0*.*\0\0"); ext_strlen = safe_strlen(ext_desc) + 2*safe_strlen(ext) + sizeof(" (*.)\0*.\0All Files (*.*)\0*.*\0\0");
ext_string = (char*)malloc(ext_strlen); ext_string = (char*)malloc(ext_strlen);
safe_sprintf(ext_string, ext_strlen, "%s (*.%s)\r*.%s\rAll Files (*.*)\r*.*\r\0", ext_desc, ext, ext); safe_sprintf(ext_string, ext_strlen, "%s (*.%s)\r*.%s\rAll Files (*.*)\r*.*\r\0", ext_desc, ext, ext);
// Microsoft could really have picked a better delimiter! // Microsoft could really have picked a better delimiter!
@ -1115,7 +1115,6 @@ INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM l
{ {
HWND hPolicy; HWND hPolicy;
static HWND hFrequency, hBeta; static HWND hFrequency, hBeta;
DWORD frequency = -1;
switch (message) { switch (message) {
case WM_INITDIALOG: case WM_INITDIALOG:
@ -1191,3 +1190,72 @@ BOOL SetUpdateCheck(void)
// TODO: make sure we check for updates if user just accepted // TODO: make sure we check for updates if user just accepted
return TRUE; return TRUE;
} }
/*
* New version notification dialog
*/
static const char* release_notes;
static const char* download_url;
INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
int i;
HWND hNotes;
TEXTRANGEW tr;
ENLINK* enl;
wchar_t wUrl[256];
char* filepath;
switch (message) {
case WM_INITDIALOG:
CenterDialog(hDlg);
hNotes = GetDlgItem(hDlg, IDC_RELEASE_NOTES);
SendMessage(hNotes, EM_AUTOURLDETECT, 1, 0);
SendMessageA(hNotes, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)release_notes);
SendMessage(hNotes, EM_SETSEL, -1, -1);
SendMessage(hNotes, EM_SETEVENTMASK, 0, ENM_LINK);
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case EN_LINK:
enl = (ENLINK*) lParam;
if (enl->msg == WM_LBUTTONUP) {
tr.lpstrText = wUrl;
tr.chrg.cpMin = enl->chrg.cpMin;
tr.chrg.cpMax = enl->chrg.cpMax;
SendMessageW(enl->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
ShellExecuteW(hDlg, L"open", wUrl, NULL, NULL, SW_SHOWNORMAL);
}
break;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDCLOSE:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
case IDC_DOWNLOAD:
if (download_url == NULL)
return (INT_PTR)TRUE;
for (i=(int)safe_strlen(download_url); (i>0)&&(download_url[i]!='/'); i--);
filepath = FileDialog(TRUE, app_dir, (char*)&download_url[i+1], "exe", "Application");
if (filepath != NULL) {
// TODO: Do we want to close the release notes once download starts?
// EndDialog(hDlg, 0);
// SetFocus(hMainDialog);
DownloadFile(download_url, filepath);
safe_free(filepath);
}
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
INT_PTR NewVersionDialog(const char* notes, const char* url)
{
release_notes = notes;
download_url = url;
return DialogBoxA(hMainInstance, MAKEINTRESOURCEA(IDD_NEW_VERSION), hMainDialog, NewVersionCallback);
}