From 4251e78fcd5302f5fdbd2f9732049a4c13e2b6a3 Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Tue, 4 Dec 2012 01:47:45 +0000 Subject: [PATCH] [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 --- src/iso.c | 5 ++-- src/license.h | 2 +- src/net.c | 41 +++++++++++++-------------- src/parser.c | 57 ++++++++++++++++++++++--------------- src/resource.h | 9 ++++-- src/rufus.c | 51 +++++++++++++++++++-------------- src/rufus.h | 7 +++-- src/rufus.rc | 38 +++++++++++++++++-------- src/stdlg.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++--- 9 files changed, 196 insertions(+), 90 deletions(-) diff --git a/src/iso.c b/src/iso.c index d7a7841f..6e9c93a0 100644 --- a/src/iso.c +++ b/src/iso.c @@ -417,8 +417,7 @@ BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan) SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); } - ShowWindow(hISOProgressDlg, SW_SHOW); - UpdateWindow(hISOProgressDlg); + SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0); /* First try to open as UDF - fallback to ISO if it failed */ p_udf = udf_open(src_iso); @@ -514,7 +513,7 @@ out: if (fd != NULL) fclose(fd); } - ShowWindow(hISOProgressDlg, SW_HIDE); + SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); if (p_iso != NULL) iso9660_close(p_iso); if (p_udf != NULL) diff --git a/src/license.h b/src/license.h index d2baad9e..b6bf8d82 100644 --- a/src/license.h +++ b/src/license.h @@ -85,7 +85,7 @@ const char* additional_copyrights = "All other references can be found in the source.\\line\n}"; 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" "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" diff --git a/src/net.c b/src/net.c index c2f78f27..6c69dc5d 100644 --- a/src/net.c +++ b/src/net.c @@ -252,13 +252,12 @@ BOOL DownloadFile(const char* url, const char* file) progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE); SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); - ShowWindow(hISOProgressDlg, SW_SHOW); - UpdateWindow(hISOProgressDlg); + SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0); PrintStatus(0, FALSE, "Downloading %s: Connecting...\n", file); 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()); goto out; } @@ -348,7 +347,7 @@ BOOL DownloadFile(const char* url, const char* file) } out: - ShowWindow(hISOProgressDlg, SW_HIDE); + SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); if (fd != NULL) fclose(fd); if (!r) { _unlink(file); @@ -383,15 +382,17 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param) BOOL is_x64 = FALSE, (__stdcall *pIsWow64Process)(HANDLE, PBOOL) = NULL; // Wait a while before checking for updates - // TODO: start the thread in sleep mode and only wake it on user inactivity? -// Sleep(15000); + // TODO: Also check on inactivity + do { + Sleep(10000); + } while (iso_op_in_progress || format_op_in_progress); -// verbose = ReadRegistryKey32(REGKEY_VERBOSE_UPDATES); -// TODO: reenable this -// if (GetRegistryKeyBool(REGKEY_DISABLE_UPDATES)) { -// vuprintf("Check for updates disabled, as per registry settings.\n"); -// return FALSE; -// } + // TODO: reenable this + // verbose = ReadRegistryKey32(REGKEY_VERBOSE_UPDATES); + if ((ReadRegistryKey32(REGKEY_UPDATE_INTERVAL) == -1)) { + vuprintf("Check for updates disabled, as per registry settings.\n"); + goto out; + } reg_time = ReadRegistryKey64(REGKEY_LAST_UPDATE); update_interval = (int64_t)ReadRegistryKey32(REGKEY_UPDATE_INTERVAL); if (update_interval == 0) { @@ -405,14 +406,14 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param) vvuprintf("Local time: %" PRId64 "\n", local_time); if (local_time < reg_time + update_interval) { 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"); if (!GetVersionExA(&os_version)) { 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 @@ -427,7 +428,7 @@ DWORD WINAPI CheckForUpdatesThread(LPVOID param) 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; _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, archname[is_x64?1:0], os_version.dwMajorVersion, os_version.dwMinorVersion); vuprintf("Base update check: %s\n", urlpath); - for (i=0, j=safe_strlen(urlpath)-5; (j>0)&&(i0)&&(i 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) { - 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); @@ -533,11 +534,9 @@ out: BOOL CheckForUpdates(void) { - // TODO: check registry for disabled update if (CreateThread(NULL, 0, CheckForUpdatesThread, NULL, 0, 0) == NULL) { uprintf("Unable to start check for updates thread"); - FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|APPERR(ERROR_CANT_START_THREAD); + return FALSE; } - return TRUE; } diff --git a/src/parser.c b/src/parser.c index 76306dee..532b11ad 100644 --- a/src/parser.c +++ b/src/parser.c @@ -40,7 +40,7 @@ typedef struct { char* platform; // target platform ("windows", "linux", etc.) char* platform_arch; // "x86", "x64", "arm" char* platform_min; // minimum platform version required - char* download_url[2]; + char* download_url; char* release_notes; } rufus_update; @@ -51,16 +51,12 @@ typedef struct { 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 weol[] = L"\r\n"; size_t i, r; BOOLEAN quoteth = FALSE; if ((wtoken == NULL) || (wline == NULL) || (wline[0] == 0)) return NULL; - // Eliminate trailing EOL characters - wline[wcscspn(wline, weol)] = 0; - i = 0; // Skip leading spaces @@ -98,6 +94,10 @@ static wchar_t* get_token_data_line(const wchar_t* wtoken, wchar_t* wline) i++; 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]; } @@ -150,7 +150,7 @@ out: // 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) { - unsigned int j; + unsigned int j, curly_count; wchar_t *wtoken = NULL, *wdata = NULL, *wbuffer = NULL, *wline = NULL; size_t i; 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)) 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); ) { 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) { done = TRUE; } 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. // 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. -// 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) { size_t i; char *data = NULL, *token; - char allowed_chars[] = " \t\r\nabcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&*()-_+=<>(){}[].,:;#@'/?|~"; + char allowed_rtf_chars[] = "abcdefghijklmnopqrstuvwxyz|~-_:*'"; + char allowed_std_chars[] = "\r\n ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&+=<>(){}[].,;#@/?"; 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; - // 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 restore it - SetFocus(hDlg); - } if (CreateThread(NULL, 0, ISOScanThread, NULL, 0, 0) == NULL) { uprintf("Unable to start ISO scanning 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; } FormatStatus = 0; + format_op_in_progress = TRUE; // Reset all progress bars SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0); 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); FormatStatus = 0; 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); if (format_thid == NULL) { 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); } } + if (format_thid == NULL) + format_op_in_progress = FALSE; break; default: return (INT_PTR)FALSE; @@ -1870,6 +1866,17 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA PostQuitMessage(0); 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: format_thid = NULL; // 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), (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)FALSE; diff --git a/src/rufus.h b/src/rufus.h index 586e6f2f..4a2164bc 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -85,6 +85,9 @@ extern void _uprintf(const char *format, ...); /* Custom Windows messages */ enum user_message_type { UM_FORMAT_COMPLETED = WM_APP, + // TODO: relabel "ISO" to a more generic "progress" + UM_ISO_CREATE, + UM_ISO_INIT, UM_ISO_EXIT }; @@ -202,12 +205,12 @@ extern HWND hMainDialog, hLogDlg, hStatus, hDeviceList, hCapacity; extern HWND hFileSystem, hClusterSize, hLabel, hDOSType, hNBPasses, hLog; extern HWND hISOProgressDlg, hISOProgressBar, hISOFileName, hDiskID; extern float fScale; -extern char szFolderPath[MAX_PATH]; +extern char szFolderPath[MAX_PATH], app_dir[MAX_PATH]; extern char* iso_path; extern DWORD FormatStatus; extern RUFUS_DRIVE_INFO SelectedDrive; 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 int64_t iso_blocking_status; extern int rufus_version[4]; diff --git a/src/rufus.rc b/src/rufus.rc index 80dddc1f..2d749b5a 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -30,7 +30,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 206, 316 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_APPWINDOW -CAPTION "Rufus v1.2.1.201" +CAPTION "Rufus v1.2.1.202" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -74,8 +74,8 @@ BEGIN ICON IDI_ICON,IDC_ABOUT_ICON,11,8,20,20 DEFPUSHBUTTON "OK",IDOK,231,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_BLURB,"RichEdit20W",ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_TABSTOP,46,7,235,93 + CONTROL "",IDC_ABOUT_COPYRIGHTS,"RichEdit20W",WS_VSCROLL | 0x804,46,101,235,74,WS_EX_STATICEDGE + 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 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 PUSHBUTTON "Clear Log",IDC_LOG_CLEAR,198,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 IDD_NOTIFICATION DIALOGEX 0, 0, 263, 63 @@ -119,8 +119,8 @@ BEGIN ICON 32516,IDC_NOTIFICATION_ICON,6,6,20,20 LTEXT "",IDC_NOTIFICATION_TEXT,35,10,219,20 DEFPUSHBUTTON "No",IDNO,206,44,50,14 - DEFPUSHBUTTON "More info...",IDC_MORE_INFO,8,44,50,14,NOT WS_VISIBLE - DEFPUSHBUTTON "Yes",IDYES,154,44,50,14,NOT WS_VISIBLE + PUSHBUTTON "More info...",IDC_MORE_INFO,8,44,50,14,NOT WS_VISIBLE + PUSHBUTTON "Yes",IDYES,154,44,50,14,NOT WS_VISIBLE END 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 BEGIN ICON IDI_ICON,IDC_ABOUT_ICON,11,8,21,20 - DEFPUSHBUTTON "Close",IDCLOSE,229,176,50,14,WS_GROUP - CONTROL "",IDC_POLICY,"RichEdit20W",ES_MULTILINE | ES_READONLY | WS_VSCROLL,46,8,235,130,WS_EX_STATICEDGE + DEFPUSHBUTTON "Close",IDCANCEL,229,176,50,14,WS_GROUP + 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 LTEXT "Check for updates (at most):",IDC_STATIC,52,157,92,11 LTEXT "Include beta versions:",IDC_STATIC,52,173,93,11 @@ -138,6 +138,16 @@ BEGIN GROUPBOX "Settings",IDC_STATIC,46,145,173,45 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 ///////////////////////////////////////////////////////////////////////////// @@ -239,6 +249,10 @@ BEGIN IDD_UPDATE_POLICY, DIALOG BEGIN END + + IDD_NEW_VERSION, DIALOG + BEGIN + END END #endif // APSTUDIO_INVOKED @@ -249,8 +263,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,1,201 - PRODUCTVERSION 1,2,1,201 + FILEVERSION 1,2,1,202 + PRODUCTVERSION 1,2,1,202 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -267,13 +281,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.2.1.201" + VALUE "FileVersion", "1.2.1.202" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", "(c) 2011-2012 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "http://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "1.2.1.201" + VALUE "ProductVersion", "1.2.1.202" END END BLOCK "VarFileInfo" diff --git a/src/stdlg.c b/src/stdlg.c index ae9a848f..2db3dce7 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -458,9 +458,9 @@ char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_des INIT_VISTA_SHELL32; if (IS_VISTA_SHELL32_AVAILABLE) { // 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) { - 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); safe_free(ext_filter); filter_spec[0].pszName = utf8_to_wchar(ext_desc); @@ -536,7 +536,7 @@ fallback: ofn.lpstrFile = selected_name; ofn.nMaxFile = MAX_PATH; // 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); 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! @@ -1115,7 +1115,6 @@ INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM l { HWND hPolicy; static HWND hFrequency, hBeta; - DWORD frequency = -1; switch (message) { case WM_INITDIALOG: @@ -1191,3 +1190,72 @@ BOOL SetUpdateCheck(void) // TODO: make sure we check for updates if user just accepted 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); +}