diff --git a/src/license.h b/src/license.h index b6bf8d82..ea9a9bba 100644 --- a/src/license.h +++ b/src/license.h @@ -81,6 +81,10 @@ const char* additional_copyrights = "http://winscp.net\\line\n" "GNU General Public License (GPL) v3 or later\\line\n" "\\line\n" +"Check for Update dialog inspired by TortoiseSVN & TortoiseGit\\line\n" +"http://tortoisesvn.net/, http://code.google.com/p/tortoisegit/\\line\n" +"GNU General Public License (GPL) v2 or later\\line\n" +"\\line\n" "\\line\n" "All other references can be found in the source.\\line\n}"; diff --git a/src/net.c b/src/net.c index 6c69dc5d..f6ccd03a 100644 --- a/src/net.c +++ b/src/net.c @@ -233,7 +233,7 @@ const char* WinInetErrorString(void) * Download a file from an URL * Mostly taken from http://support.microsoft.com/kb/234913 */ -BOOL DownloadFile(const char* url, const char* file) +BOOL DownloadFile(const char* url, const char* file, HWND hProgressDialog, HWND hProgressBar) { BOOL r = FALSE; DWORD dwFlags, dwSize, dwDownloaded, dwTotalSize, dwStatus; @@ -247,12 +247,15 @@ BOOL DownloadFile(const char* url, const char* file) int i; // We reuse the ISO progress dialog for download progress - SetWindowTextU(hISOProgressDlg, "Downloading file..."); - SetWindowTextU(hISOFileName, url); - progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE); - SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); - SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0); - SendMessage(hISOProgressDlg, UM_ISO_INIT, 0, 0); + if (hProgressDialog != NULL) + SetWindowTextU(hProgressDialog, "Downloading file..."); + if (hProgressBar != NULL) { + progress_style = GetWindowLong(hProgressBar, GWL_STYLE); + SetWindowLong(hProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE)); + SendMessage(hProgressBar, PBM_SETPOS, 0, 0); + } + if (hProgressDialog != NULL) + SendMessage(hProgressDialog, UM_ISO_INIT, 0, 0); PrintStatus(0, FALSE, "Downloading %s: Connecting...\n", file); uprintf("Downloading %s from %s\n", file, url); @@ -329,7 +332,7 @@ BOOL DownloadFile(const char* url, const char* file) if (!InternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0)) break; dwSize += dwDownloaded; - SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize)/(1.0f*dwTotalSize))), 0); + SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwSize)/(1.0f*dwTotalSize))), 0); PrintStatus(0, FALSE, "Downloading %s: %0.1f%%\n", file, (100.0f*dwSize)/(1.0f*dwTotalSize)); if (fwrite(buf, 1, dwDownloaded, fd) != dwDownloaded) { uprintf("Error writing file %s: %s\n", file, WinInetErrorString()); @@ -347,7 +350,8 @@ BOOL DownloadFile(const char* url, const char* file) } out: - SendMessage(hISOProgressDlg, UM_ISO_EXIT, 0, 0); + if (hProgressDialog != NULL) + SendMessage(hProgressDialog, UM_ISO_EXIT, 0, 0); if (fd != NULL) fclose(fd); if (!r) { _unlink(file); diff --git a/src/parser.c b/src/parser.c index 532b11ad..f40a3ed6 100644 --- a/src/parser.c +++ b/src/parser.c @@ -34,17 +34,6 @@ #include "rufus.h" #include "msapi_utf8.h" -typedef struct { - uint8_t version[4]; - char* type; // "release", "beta", "notice" - char* platform; // target platform ("windows", "linux", etc.) - char* platform_arch; // "x86", "x64", "arm" - char* platform_min; // minimum platform version required - char* download_url; - char* release_notes; -} rufus_update; - - // Parse a line of UTF-16 text and return the data if it matches the 'token' // The parsed line is of the form: [ ]token[ ]=[ ]["]data["][ ] and the line // is modified by the parser @@ -214,14 +203,12 @@ static __inline char* get_sanitized_token_data_buffer(const char* token, unsigne // 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, 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_rtf_chars[] = "abcdefghijklmnopqrstuvwxyz|~-_:*'"; char allowed_std_chars[] = "\r\n ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"$%^&+=<>(){}[].,;#@/?"; - rufus_update update; // 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] == '\\')) @@ -264,8 +251,7 @@ void parse_update(char* buf, size_t len) while (iso_op_in_progress || format_op_in_progress) { Sleep(3000); } - NewVersionDialog(update.release_notes, update.download_url); - + DownloadNewVersion(); // TODO: free all these strings! } diff --git a/src/resource.h b/src/resource.h index 43637765..f1d651db 100644 --- a/src/resource.h +++ b/src/resource.h @@ -89,6 +89,11 @@ #define IDC_INCLUDE_BETAS 1063 #define IDC_RELEASE_NOTES 1064 #define IDC_DOWNLOAD 1065 +#define IDC_UPDATE_PROGRESS 1066 +#define IDC_WEBSITE 1067 +#define IDC_YOUR_VERSION 1068 +#define IDC_LATEST_VERSION 1069 +#define IDC_DOWNLOAD_URL 1070 // Next default values for new objects // @@ -97,7 +102,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 113 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1066 +#define _APS_NEXT_CONTROL_VALUE 1071 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/src/rufus.c b/src/rufus.c index 599c6940..ff52c762 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -106,6 +106,7 @@ HWND hISOProgressDlg = NULL, hLogDlg = NULL, hISOProgressBar, hISOFileName, hDis 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]; +RUFUS_UPDATE update; extern char szStatusMessage[256]; static HANDLE format_thid = NULL; @@ -1282,7 +1283,8 @@ DWORD WINAPI ISOScanThread(LPVOID param) "Note: the file will be downloaded in the current directory. Once a\n" "vesamenu.c32 exists there, it will always be used as replacement.\n", "Replace vesamenu.c32?", MB_YESNO|MB_ICONWARNING) == IDYES) { - if (DownloadFile(VESAMENU_URL, vesamenu_filename)) + SetWindowTextU(hISOFileName, VESAMENU_URL); + if (DownloadFile(VESAMENU_URL, vesamenu_filename, hISOProgressDlg, hISOProgressBar)) use_own_vesamenu = TRUE; } } @@ -1371,7 +1373,6 @@ void InitDialog(HWND hDlg) HINSTANCE hDllInst; HDC hDC; int i, i16, s16, s32; - HICON hSmallIcon, hBigIcon; char tmp[128], *token; #ifdef RUFUS_TEST @@ -1418,10 +1419,7 @@ void InitDialog(HWND hDlg) s32 = 24; // Create the title bar icon - hSmallIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s16, s16, 0); - SendMessage (hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon); - hBigIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s32, s32, 0); - SendMessage (hDlg, WM_SETICON, ICON_BIG, (LPARAM)hBigIcon); + SetTitleBarIcon(hDlg); GetWindowTextA(hDlg, tmp, sizeof(tmp)); // Count of Microsoft for making it more attractive to read a // version using strtok() than using GetFileVersionInfo() diff --git a/src/rufus.h b/src/rufus.h index 4a2164bc..e503a5e7 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -175,6 +175,16 @@ typedef struct { BOOL uses_minint; } RUFUS_ISO_REPORT; +typedef struct { + uint8_t version[4]; + char* type; // "release", "beta", "notice" + char* platform; // target platform ("windows", "linux", etc.) + char* platform_arch; // "x86", "x64", "arm" + char* platform_min; // minimum platform version required + char* download_url; + char* release_notes; +} RUFUS_UPDATE; + /* Duplication of the TBPFLAG enum for Windows 7 taskbar progress */ typedef enum TASKBAR_PROGRESS_FLAGS { @@ -215,6 +225,7 @@ extern RUFUS_ISO_REPORT iso_report; extern int64_t iso_blocking_status; extern int rufus_version[4]; extern enum WindowsVersion nWindowsVersion; +extern RUFUS_UPDATE update; /* * Shared prototypes @@ -227,6 +238,7 @@ extern void UpdateProgress(int op, float percent); extern const char* StrError(DWORD error_code); extern void CenterDialog(HWND hDlg); extern void CreateStatusBar(void); +extern void SetTitleBarIcon(HWND hDlg); extern BOOL CreateTaskbarList(void); extern BOOL SetTaskbarProgressState(TASKBAR_PROGRESS_FLAGS tbpFlags); extern BOOL SetTaskbarProgressValue(ULONGLONG ullCompleted, ULONGLONG ullTotal); @@ -250,10 +262,11 @@ extern BOOL SetAutorun(const char* path); extern char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_desc); extern BOOL FileIO(BOOL save, char* path, char** buffer, DWORD* size); extern LONG GetEntryWidth(HWND hDropDown, const char* entry); -extern BOOL DownloadFile(const char* url, const char* file); +extern BOOL DownloadFile(const char* url, const char* file, HWND hProgressDialog, HWND hProgressBar); extern INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); extern BOOL SetUpdateCheck(void); extern BOOL CheckForUpdates(void); +extern void DownloadNewVersion(void); extern BOOL IsShown(HWND hDlg); extern char* get_token_data_file(const char* token, const char* filename); extern char* get_token_data_buffer(const char* token, unsigned int n, const char* buffer, size_t buffer_size); diff --git a/src/rufus.rc b/src/rufus.rc index 2d749b5a..781cefff 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.202" +CAPTION "Rufus v1.2.1.203" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Start",IDC_START,94,278,50,14 @@ -138,14 +138,23 @@ BEGIN GROUPBOX "Settings",IDC_STATIC,46,145,173,45 END -IDD_NEW_VERSION DIALOGEX 0, 0, 287, 198 +IDD_NEW_VERSION DIALOGEX 0, 0, 384, 268 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "New version available" +CAPTION "Check For Updates - Rufus" 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 + PUSHBUTTON "Close",IDCANCEL,167,244,50,14,WS_GROUP + CONTROL "",IDC_RELEASE_NOTES,"RichEdit20W",ES_MULTILINE | ES_READONLY | WS_VSCROLL,15,77,352,88,WS_EX_STATICEDGE + DEFPUSHBUTTON "Download",IDC_DOWNLOAD,293,211,74,14,WS_GROUP + CONTROL "",IDC_UPDATE_PROGRESS,"msctls_progress32",WS_BORDER,15,212,270,11 + GROUPBOX "Release Notes",IDC_STATIC,8,63,367,111 + LTEXT "A newer version is available. Please download the latest version!",IDC_STATIC,10,32,229,8 + LTEXT "[...]",IDC_YOUR_VERSION,10,8,124,8 + LTEXT "[...]",IDC_LATEST_VERSION,10,19,129,8 + CONTROL "Click here to go to the website",IDC_WEBSITE, + "SysLink",WS_TABSTOP,143,49,96,9 + GROUPBOX "Download",IDC_STATIC,8,177,367,58 + EDITTEXT IDC_DOWNLOAD_URL,15,191,351,13,ES_AUTOHSCROLL | ES_READONLY END @@ -263,8 +272,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,1,202 - PRODUCTVERSION 1,2,1,202 + FILEVERSION 1,2,1,203 + PRODUCTVERSION 1,2,1,203 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -281,13 +290,13 @@ BEGIN BEGIN VALUE "CompanyName", "Akeo Consulting (http://akeo.ie)" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "1.2.1.202" + VALUE "FileVersion", "1.2.1.203" 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.202" + VALUE "ProductVersion", "1.2.1.203" END END BLOCK "VarFileInfo" diff --git a/src/stdlg.c b/src/stdlg.c index 2db3dce7..b4cf45d8 100644 --- a/src/stdlg.c +++ b/src/stdlg.c @@ -662,6 +662,7 @@ INT_PTR CALLBACK AboutCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lP switch (message) { case WM_INITDIALOG: + SetTitleBarIcon(hDlg); CenterDialog(hDlg); if (reg_commcheck) ShowWindow(GetDlgItem(hDlg, IDC_ABOUT_UPDATES), SW_SHOW); @@ -733,6 +734,7 @@ INT_PTR CALLBACK NotificationCallback(HWND hDlg, UINT message, WPARAM wParam, LP case WM_INITDIALOG: white_brush = CreateSolidBrush(WHITE); separator_brush = CreateSolidBrush(SEPARATOR_GREY); + SetTitleBarIcon(hDlg); CenterDialog(hDlg); // Change the default icon if (Static_SetIcon(GetDlgItem(hDlg, IDC_NOTIFICATION_ICON), hMessageIcon) == 0) { @@ -1118,6 +1120,7 @@ INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM l switch (message) { case WM_INITDIALOG: + SetTitleBarIcon(hDlg); CenterDialog(hDlg); hFrequency = GetDlgItem(hDlg, IDC_UPDATE_FREQUENCY); IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, "Disabled"), -1)); @@ -1194,8 +1197,6 @@ BOOL SetUpdateCheck(void) /* * 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; @@ -1203,19 +1204,36 @@ INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPAR TEXTRANGEW tr; ENLINK* enl; wchar_t wUrl[256]; + char tmp[128]; char* filepath; + static BOOL was_downloaded = FALSE; switch (message) { case WM_INITDIALOG: + SetTitleBarIcon(hDlg); 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); + SendMessageA(hNotes, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)update.release_notes); SendMessage(hNotes, EM_SETSEL, -1, -1); SendMessage(hNotes, EM_SETEVENTMASK, 0, ENM_LINK); + safe_sprintf(tmp, sizeof(tmp), "Your version: %d.%d.%d (Build %d)", + rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]); + SetWindowTextA(GetDlgItem(hDlg, IDC_YOUR_VERSION), tmp); + safe_sprintf(tmp, sizeof(tmp), "Latest version: %d.%d.%d (Build %d)", + update.version[0], update.version[1], update.version[2], update.version[3]); + SetWindowTextA(GetDlgItem(hDlg, IDC_LATEST_VERSION), tmp); + SetWindowTextA(GetDlgItem(hDlg, IDC_DOWNLOAD_URL), update.download_url); + SendMessage(GetDlgItem(hDlg, IDC_UPDATE_PROGRESS), PBM_SETRANGE, 0, MAX_PROGRESS<<16); break; case WM_NOTIFY: switch (((LPNMHDR)lParam)->code) { + case NM_CLICK: + case NM_RETURN: + if (LOWORD(wParam) == IDC_WEBSITE) { + ShellExecuteA(hDlg, "open", RUFUS_URL, NULL, NULL, SW_SHOWNORMAL); + } + break; case EN_LINK: enl = (ENLINK*) lParam; if (enl->msg == WM_LBUTTONUP) { @@ -1235,15 +1253,16 @@ INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPAR EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case IDC_DOWNLOAD: - if (download_url == NULL) + if (update.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"); + for (i=(int)safe_strlen(update.download_url); (i>0)&&(update.download_url[i]!='/'); i--); + filepath = FileDialog(TRUE, app_dir, (char*)&update.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); + if (DownloadFile(update.download_url, filepath, NULL, GetDlgItem(hDlg, IDC_UPDATE_PROGRESS))) { + // TODO: create a thread and allow aborts + invoke a launcher when successful + SetWindowTextA(GetDlgItem(hDlg, IDC_DOWNLOAD), "Done"); + EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE); + } safe_free(filepath); } return (INT_PTR)TRUE; @@ -1253,9 +1272,45 @@ INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPAR return (INT_PTR)FALSE; } -INT_PTR NewVersionDialog(const char* notes, const char* url) +void DownloadNewVersion(void) { - release_notes = notes; - download_url = url; - return DialogBoxA(hMainInstance, MAKEINTRESOURCEA(IDD_NEW_VERSION), hMainDialog, NewVersionCallback); + DialogBoxA(hMainInstance, MAKEINTRESOURCEA(IDD_NEW_VERSION), hMainDialog, NewVersionCallback); +} + +void SetTitleBarIcon(HWND hDlg) +{ + HDC hDC; + int i16, s16, s32; + HICON hSmallIcon, hBigIcon; + + // High DPI scaling + i16 = GetSystemMetrics(SM_CXSMICON); + hDC = GetDC(hDlg); + fScale = GetDeviceCaps(hDC, LOGPIXELSX) / 96.0f; + ReleaseDC(hDlg, hDC); + // Adjust icon size lookup + s16 = i16; + s32 = (int)(32.0f*fScale); + if (s16 >= 54) + s16 = 64; + else if (s16 >= 40) + s16 = 48; + else if (s16 >= 28) + s16 = 32; + else if (s16 >= 20) + s16 = 24; + if (s32 >= 54) + s32 = 64; + else if (s32 >= 40) + s32 = 48; + else if (s32 >= 28) + s32 = 32; + else if (s32 >= 20) + s32 = 24; + + // Create the title bar icon + hSmallIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s16, s16, 0); + SendMessage (hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon); + hBigIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s32, s32, 0); + SendMessage (hDlg, WM_SETICON, ICON_BIG, (LPARAM)hBigIcon); }