Add more options to version.txt
[TortoiseGit.git] / src / TortoiseProc / CheckForUpdatesDlg.cpp
bloba32d5761d408423de2b86535106edbaffbc54b5c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2014 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "TortoiseProc.h"
22 #include "LoglistCommonResource.h"
23 #include "..\version.h"
24 #include "MessageBox.h"
25 #include "CheckForUpdatesDlg.h"
26 #include "registry.h"
27 #include "AppUtils.h"
28 #include "TempFile.h"
29 #include "SmartHandle.h"
30 #include "SysInfo.h"
31 #include "PathUtils.h"
32 #include "DirFileEnum.h"
33 #include "UnicodeUtils.h"
34 #include "UpdateCrypto.h"
35 #include "Win7.h"
37 #define SIGNATURE_FILE_ENDING _T(".rsa.asc")
39 #define WM_USER_DISPLAYSTATUS (WM_USER + 1)
40 #define WM_USER_ENDDOWNLOAD (WM_USER + 2)
41 #define WM_USER_FILLCHANGELOG (WM_USER + 3)
43 IMPLEMENT_DYNAMIC(CCheckForUpdatesDlg, CStandAloneDialog)
44 CCheckForUpdatesDlg::CCheckForUpdatesDlg(CWnd* pParent /*=NULL*/)
45 : CStandAloneDialog(CCheckForUpdatesDlg::IDD, pParent)
46 , m_bShowInfo(FALSE)
47 , m_bForce(FALSE)
48 , m_bVisible(FALSE)
49 , m_pDownloadThread(NULL)
50 , m_bThreadRunning(FALSE)
51 , m_updateDownloader(nullptr)
53 m_sUpdateDownloadLink = _T("http://redir.tortoisegit.org/download");
56 CCheckForUpdatesDlg::~CCheckForUpdatesDlg()
60 void CCheckForUpdatesDlg::DoDataExchange(CDataExchange* pDX)
62 CStandAloneDialog::DoDataExchange(pDX);
63 DDX_Control(pDX, IDC_LINK, m_link);
64 DDX_Control(pDX, IDC_PROGRESSBAR, m_progress);
65 DDX_Control(pDX, IDC_LIST_DOWNLOADS, m_ctrlFiles);
66 DDX_Control(pDX, IDC_BUTTON_UPDATE, m_ctrlUpdate);
67 DDX_Control(pDX, IDC_LOGMESSAGE, m_cLogMessage);
70 BEGIN_MESSAGE_MAP(CCheckForUpdatesDlg, CStandAloneDialog)
71 ON_WM_TIMER()
72 ON_WM_WINDOWPOSCHANGING()
73 ON_WM_SETCURSOR()
74 ON_WM_DESTROY()
75 ON_BN_CLICKED(IDC_BUTTON_UPDATE, OnBnClickedButtonUpdate)
76 ON_MESSAGE(WM_USER_DISPLAYSTATUS, OnDisplayStatus)
77 ON_MESSAGE(WM_USER_ENDDOWNLOAD, OnEndDownload)
78 ON_MESSAGE(WM_USER_FILLCHANGELOG, OnFillChangelog)
79 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
80 END_MESSAGE_MAP()
82 BOOL CCheckForUpdatesDlg::OnInitDialog()
84 CStandAloneDialog::OnInitDialog();
85 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
87 CString temp;
88 temp.Format(IDS_CHECKNEWER_YOURVERSION, TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD);
89 SetDlgItemText(IDC_YOURVERSION, temp);
91 DialogEnableWindow(IDOK, FALSE);
93 m_pTaskbarList.Release();
94 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
95 m_pTaskbarList = nullptr;
97 // hide download controls
98 m_ctrlFiles.ShowWindow(SW_HIDE);
99 GetDlgItem(IDC_GROUP_DOWNLOADS)->ShowWindow(SW_HIDE);
100 RECT rectWindow, rectGroupDownloads, rectOKButton;
101 GetWindowRect(&rectWindow);
102 GetDlgItem(IDC_GROUP_DOWNLOADS)->GetWindowRect(&rectGroupDownloads);
103 GetDlgItem(IDOK)->GetWindowRect(&rectOKButton);
104 LONG bottomDistance = rectWindow.bottom - rectOKButton.bottom;
105 OffsetRect(&rectOKButton, 0, rectGroupDownloads.top - rectOKButton.top);
106 rectWindow.bottom = rectOKButton.bottom + bottomDistance;
107 MoveWindow(&rectWindow);
108 ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT)&rectOKButton, 2);
109 GetDlgItem(IDOK)->MoveWindow(&rectOKButton);
111 temp.LoadString(IDS_STATUSLIST_COLFILE);
112 m_ctrlFiles.InsertColumn(0, temp, 0, -1);
113 m_ctrlFiles.InsertColumn(1, temp, 0, -1);
114 m_ctrlFiles.SetExtendedStyle(LVS_EX_DOUBLEBUFFER | LVS_EX_CHECKBOXES);
115 m_ctrlFiles.SetColumnWidth(0, 350);
116 m_ctrlFiles.SetColumnWidth(1, 200);
118 m_cLogMessage.Init(-1);
119 m_cLogMessage.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
120 m_cLogMessage.Call(SCI_SETREADONLY, TRUE);
122 m_updateDownloader = new CUpdateDownloader(GetSafeHwnd(), m_bForce == TRUE, WM_USER_DISPLAYSTATUS, &m_eventStop);
124 if (AfxBeginThread(CheckThreadEntry, this)==NULL)
126 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
129 SetTimer(100, 1000, NULL);
130 return TRUE;
133 void CCheckForUpdatesDlg::OnDestroy()
135 for (int i = 0; i < m_ctrlFiles.GetItemCount(); ++i)
136 delete (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
138 if (m_updateDownloader)
139 delete m_updateDownloader;
141 CStandAloneDialog::OnDestroy();
144 void CCheckForUpdatesDlg::OnOK()
146 if (m_bThreadRunning || m_pDownloadThread != NULL)
147 return; // Don't exit while downloading
149 CStandAloneDialog::OnOK();
152 void CCheckForUpdatesDlg::OnCancel()
154 if (m_bThreadRunning || m_pDownloadThread != NULL)
155 return; // Don't exit while downloading
156 CStandAloneDialog::OnCancel();
159 UINT CCheckForUpdatesDlg::CheckThreadEntry(LPVOID pVoid)
161 return ((CCheckForUpdatesDlg*)pVoid)->CheckThread();
164 UINT CCheckForUpdatesDlg::CheckThread()
166 m_bThreadRunning = TRUE;
168 CString temp;
169 CString tempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
171 bool official = false;
173 CRegString checkurluser = CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""));
174 CRegString checkurlmachine = CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""), FALSE, HKEY_LOCAL_MACHINE);
175 CString sCheckURL = checkurluser;
176 if (sCheckURL.IsEmpty())
178 sCheckURL = checkurlmachine;
179 if (sCheckURL.IsEmpty())
181 official = true;
182 bool checkPreview = false;
183 #if PREVIEW
184 checkPreview = true;
185 #else
186 CRegStdDWORD regCheckPreview(L"Software\\TortoiseGit\\VersionCheckPreview", FALSE);
187 if (DWORD(regCheckPreview))
188 checkPreview = true;
189 #endif
190 if (checkPreview)
192 sCheckURL = _T("://versioncheck.tortoisegit.org/version-preview.txt");
193 SetDlgItemText(IDC_SOURCE, _T("Using preview release channel"));
195 else
196 sCheckURL = _T("://versioncheck.tortoisegit.org/version.txt");
197 if (SysInfo::Instance().IsVistaOrLater()) // we need SNI support
198 sCheckURL = _T("https") + sCheckURL;
199 else
200 sCheckURL = _T("http") + sCheckURL;
204 if (!official)
205 SetDlgItemText(IDC_SOURCE, _T("Using (unofficial) release channel: ") + sCheckURL);
207 CString ver;
208 CAutoConfig versioncheck(true);
209 CString errorText;
210 BOOL ret = m_updateDownloader->DownloadFile(sCheckURL, tempfile, false);
211 if (!ret && official)
213 CString signatureTempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
214 ret = m_updateDownloader->DownloadFile(sCheckURL + SIGNATURE_FILE_ENDING, signatureTempfile, false);
215 if (!ret && VerifyIntegrity(tempfile, signatureTempfile, m_updateDownloader))
217 SetDlgItemText(IDC_CHECKRESULT, _T("Could not verify digital signature."));
218 DeleteUrlCacheEntry(sCheckURL);
219 DeleteUrlCacheEntry(sCheckURL + SIGNATURE_FILE_ENDING);
220 goto finish;
223 else if (ret)
225 DeleteUrlCacheEntry(sCheckURL);
226 if (CRegDWORD(_T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\GlobalUserOffline"), 0))
227 errorText.LoadString(IDS_OFFLINEMODE); // offline mode enabled
228 else
229 errorText.Format(IDS_CHECKNEWER_NETERROR_FORMAT, ret);
230 SetDlgItemText(IDC_CHECKRESULT, errorText);
231 goto finish;
234 git_config_add_file_ondisk(versioncheck, CUnicodeUtils::GetUTF8(tempfile), GIT_CONFIG_LEVEL_GLOBAL, 0);
236 unsigned int major, minor, micro, build;
237 major = minor = micro = build = 0;
238 unsigned __int64 version = 0;
240 if (!versioncheck.GetString(_T("tortoisegit.version"), ver))
242 CString vertemp = ver;
243 major = _ttoi(vertemp);
244 vertemp = vertemp.Mid(vertemp.Find('.') + 1);
245 minor = _ttoi(vertemp);
246 vertemp = vertemp.Mid(vertemp.Find('.') + 1);
247 micro = _ttoi(vertemp);
248 vertemp = vertemp.Mid(vertemp.Find('.') + 1);
249 build = _ttoi(vertemp);
250 version = major;
251 version <<= 16;
252 version += minor;
253 version <<= 16;
254 version += micro;
255 version <<= 16;
256 version += build;
258 if (version == 0)
260 temp.LoadString(IDS_CHECKNEWER_NETERROR);
261 SetDlgItemText(IDC_CHECKRESULT, temp);
262 goto finish;
265 // another versionstring for the filename can be provided
266 // this is needed for preview releases
267 vertemp.Empty();
268 versioncheck.GetString(_T("tortoisegit.versionstring"), vertemp);
269 if (!vertemp.IsEmpty())
270 ver = vertemp;
272 else
274 errorText = _T("Could not parse version check file: ") + g_Git.GetLibGit2LastErr();
275 SetDlgItemText(IDC_CHECKRESULT, errorText);
276 DeleteUrlCacheEntry(sCheckURL);
277 goto finish;
281 BOOL bNewer = FALSE;
282 if (m_bForce)
283 bNewer = TRUE;
284 else if (major > TGIT_VERMAJOR)
285 bNewer = TRUE;
286 else if ((minor > TGIT_VERMINOR) && (major == TGIT_VERMAJOR))
287 bNewer = TRUE;
288 else if ((micro > TGIT_VERMICRO) && (minor == TGIT_VERMINOR) && (major == TGIT_VERMAJOR))
289 bNewer = TRUE;
290 else if ((build > TGIT_VERBUILD) && (micro == TGIT_VERMICRO) && (minor == TGIT_VERMINOR) && (major == TGIT_VERMAJOR))
291 bNewer = TRUE;
293 CString versionstr;
294 versionstr.Format(_T("%u.%u.%u.%u"), major, minor, micro, build);
295 if (versionstr != ver)
296 versionstr += _T(" (") + ver + _T(")");
297 temp.Format(IDS_CHECKNEWER_CURRENTVERSION, (LPCTSTR)versionstr);
298 SetDlgItemText(IDC_CURRENTVERSION, temp);
300 if (bNewer)
302 versioncheck.GetString(_T("tortoisegit.infotext"), temp);
303 if (!temp.IsEmpty())
305 CString tempLink;
306 versioncheck.GetString(_T("tortoisegit.infotexturl"), tempLink);
307 if (!tempLink.IsEmpty()) // find out the download link-URL, if any
308 m_sUpdateDownloadLink = tempLink;
310 else
311 temp.LoadString(IDS_CHECKNEWER_NEWERVERSIONAVAILABLE);
312 SetDlgItemText(IDC_CHECKRESULT, temp);
314 FillChangelog(versioncheck, official);
315 FillDownloads(versioncheck, ver);
317 // Show download controls
318 RECT rectWindow, rectProgress, rectGroupDownloads, rectOKButton;
319 GetWindowRect(&rectWindow);
320 m_progress.GetWindowRect(&rectProgress);
321 GetDlgItem(IDC_GROUP_DOWNLOADS)->GetWindowRect(&rectGroupDownloads);
322 GetDlgItem(IDOK)->GetWindowRect(&rectOKButton);
323 LONG bottomDistance = rectWindow.bottom - rectOKButton.bottom;
324 OffsetRect(&rectOKButton, 0, (rectGroupDownloads.bottom + (rectGroupDownloads.bottom - rectProgress.bottom)) - rectOKButton.top);
325 rectWindow.bottom = rectOKButton.bottom + bottomDistance;
326 MoveWindow(&rectWindow);
327 ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT)&rectOKButton, 2);
328 GetDlgItem(IDOK)->MoveWindow(&rectOKButton);
329 m_ctrlFiles.ShowWindow(SW_SHOW);
330 GetDlgItem(IDC_GROUP_DOWNLOADS)->ShowWindow(SW_SHOW);
331 CenterWindow();
332 m_bShowInfo = TRUE;
334 else if (m_bShowInfo)
336 temp.LoadString(IDS_CHECKNEWER_YOURUPTODATE);
337 SetDlgItemText(IDC_CHECKRESULT, temp);
338 FillChangelog(versioncheck, official);
342 finish:
343 if (!m_sUpdateDownloadLink.IsEmpty())
345 m_link.ShowWindow(SW_SHOW);
346 m_link.SetURL(m_sUpdateDownloadLink);
348 m_bThreadRunning = FALSE;
349 DialogEnableWindow(IDOK, TRUE);
350 return 0;
353 void CCheckForUpdatesDlg::FillDownloads(CAutoConfig& versioncheck, const CString version)
355 #if WIN64
356 const CString x86x64 = _T("64");
357 #else
358 const CString x86x64 = _T("32");
359 #endif
361 versioncheck.GetString(_T("tortoisegit.baseurl"), m_sFilesURL);
362 if (m_sFilesURL.IsEmpty())
363 m_sFilesURL.Format(_T("http://updater.download.tortoisegit.org/tgit/%s/"), version);
365 m_ctrlFiles.InsertItem(0, _T("TortoiseGit"));
366 CString filenameMain, filenamePattern;
367 versioncheck.GetString(_T("tortoisegit.mainfilename"), filenamePattern);
368 if (filenamePattern.IsEmpty())
369 filenamePattern = _T("TortoiseGit-%1!s!-%2!s!bit.msi");
370 filenameMain.FormatMessage(filenamePattern, version, x86x64);
371 m_ctrlFiles.SetItemData(0, (DWORD_PTR)(new CUpdateListCtrl::Entry(filenameMain, CUpdateListCtrl::STATUS_NONE)));
372 m_ctrlFiles.SetCheck(0 , TRUE);
374 struct LangPack
376 CString m_PackName;
377 CString m_LangName;
378 DWORD m_LocaleID;
379 CString m_LangCode;
380 bool m_Installed;
382 struct LanguagePacks
384 std::vector<LangPack> availableLangs;
385 std::vector<DWORD> installedLangs;
386 } languagePacks;
388 // set up the language selecting combobox
389 CString path = CPathUtils::GetAppParentDirectory();
390 path = path + _T("Languages\\");
391 CSimpleFileFind finder(path, _T("*.dll"));
392 while (finder.FindNextFileNoDirectories())
394 CString file = finder.GetFilePath();
395 CString filename = finder.GetFileName();
396 if (filename.Left(12).CompareNoCase(_T("TortoiseProc")) == 0)
398 CString sVer = _T(STRPRODUCTVER);
399 sVer = sVer.Left(sVer.ReverseFind('.'));
400 CString sFileVer = CPathUtils::GetVersionFromFile(file);
401 sFileVer = sFileVer.Left(sFileVer.ReverseFind('.'));
402 CString sLoc = filename.Mid(12);
403 sLoc = sLoc.Left(sLoc.GetLength() - 4); // cut off ".dll"
404 if ((sLoc.Left(2) == L"32") && (sLoc.GetLength() > 5))
405 continue;
406 DWORD loc = _tstoi(filename.Mid(12));
407 languagePacks.installedLangs.push_back(loc);
412 git_config_get_multivar_foreach(versioncheck, "tortoisegit.langs", nullptr, [](const git_config_entry* configentry, void* payload) -> int
414 LanguagePacks* languagePacks = (LanguagePacks*)payload;
415 CString langs = CUnicodeUtils::GetUnicode(configentry->value);
417 int nextTokenPos = langs.Find(_T(";"), 5); // be extensible for the future
418 if (nextTokenPos > 0)
419 langs = langs.Left(nextTokenPos);
420 CString sLang = _T("TortoiseGit Language Pack ") + langs.Mid(5);
422 DWORD loc = _tstoi(langs.Mid(0, 4));
423 TCHAR buf[MAX_PATH] = { 0 };
424 GetLocaleInfo(loc, LOCALE_SNATIVELANGNAME, buf, _countof(buf));
425 CString sLang2(buf);
426 GetLocaleInfo(loc, LOCALE_SNATIVECTRYNAME, buf, _countof(buf));
427 if (buf[0])
429 sLang2 += _T(" (");
430 sLang2 += buf;
431 sLang2 += _T(")");
434 bool installed = std::find(languagePacks->installedLangs.begin(), languagePacks->installedLangs.end(), loc) != languagePacks->installedLangs.end();
435 LangPack pack = { sLang, sLang2, loc, langs.Mid(5), installed };
436 languagePacks->availableLangs.push_back(pack);
438 return 0;
439 }, &languagePacks);
440 std::stable_sort(languagePacks.availableLangs.begin(), languagePacks.availableLangs.end(), [&](const LangPack& a, const LangPack& b) -> int
442 return (a.m_Installed && !b.m_Installed) ? 1 : (!a.m_Installed && b.m_Installed) ? 0 : (a.m_PackName.Compare(b.m_PackName) < 0);
444 filenamePattern.Empty();
445 versioncheck.GetString(_T("tortoisegit.languagepackfilename"), filenamePattern);
446 if (filenamePattern.IsEmpty())
447 filenamePattern = _T("TortoiseGit-LanguagePack-%1!s!-%2!s!bit-%3!s!.msi");
448 for (auto langs : languagePacks.availableLangs)
450 int pos = m_ctrlFiles.InsertItem(m_ctrlFiles.GetItemCount(), langs.m_PackName);
451 m_ctrlFiles.SetItemText(pos, 1, langs.m_LangName);
453 CString filename;
454 filename.FormatMessage(filenamePattern, version, x86x64, langs.m_LangCode, langs.m_LocaleID);
455 m_ctrlFiles.SetItemData(pos, (DWORD_PTR)(new CUpdateListCtrl::Entry(filename, CUpdateListCtrl::STATUS_NONE)));
457 if (langs.m_Installed)
458 m_ctrlFiles.SetCheck(pos , TRUE);
460 DialogEnableWindow(IDC_BUTTON_UPDATE, TRUE);
463 void CCheckForUpdatesDlg::FillChangelog(CAutoConfig& versioncheck, bool official)
465 ProjectProperties pp;
466 pp.lProjectLanguage = -1;
467 if (versioncheck.GetString(_T("tortoisegit.issuesurl"), pp.sUrl))
468 pp.sUrl = _T("https://code.google.com/p/tortoisegit/issues/detail?id=%BUGID%");
469 if (!pp.sUrl.IsEmpty())
471 pp.SetCheckRe(_T("[Ii]ssues?:?(\\s*(,|and)?\\s*#?\\d+)+"));
472 pp.SetBugIDRe(_T("(\\d+)"));
474 m_cLogMessage.Init(pp);
476 CString sChangelogURL;
477 versioncheck.GetString(_T("TortoiseGit.changelogurl"), sChangelogURL);
478 if (sChangelogURL.IsEmpty())
479 sChangelogURL = _T("https://versioncheck.tortoisegit.org/changelog.txt");
480 else
482 CString tmp(sChangelogURL);
483 sChangelogURL.FormatMessage(tmp, TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO);
486 CString tempchangelogfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
487 if (m_updateDownloader->DownloadFile(sChangelogURL, tempchangelogfile, false))
489 ::SendMessage(m_hWnd, WM_USER_FILLCHANGELOG, 0, reinterpret_cast<LPARAM>(_T("Could not load changelog.")));
490 return;
492 if (official)
494 CString signatureTempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
495 if (m_updateDownloader->DownloadFile(sChangelogURL + SIGNATURE_FILE_ENDING, signatureTempfile, false) || VerifyIntegrity(tempchangelogfile, signatureTempfile, m_updateDownloader))
497 ::SendMessage(m_hWnd, WM_USER_FILLCHANGELOG, 0, reinterpret_cast<LPARAM>(_T("Could not verify digital signature.")));
498 DeleteUrlCacheEntry(sChangelogURL);
499 DeleteUrlCacheEntry(sChangelogURL + SIGNATURE_FILE_ENDING);
500 return;
504 CString temp;
505 CStdioFile file;
506 if (file.Open(tempchangelogfile, CFile::modeRead | CFile::typeBinary))
508 std::unique_ptr<BYTE[]> buf(new BYTE[(UINT)file.GetLength()]);
509 UINT read = file.Read(buf.get(), (UINT)file.GetLength());
510 bool skipBom = read >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF;
511 g_Git.StringAppend(&temp, buf.get() + (skipBom ? 3 : 0), CP_UTF8, read - (skipBom ? 3 : 0));
513 ::SendMessage(m_hWnd, WM_USER_FILLCHANGELOG, 0, reinterpret_cast<LPARAM>((LPCTSTR)temp));
516 void CCheckForUpdatesDlg::OnTimer(UINT_PTR nIDEvent)
518 if (nIDEvent == 100)
520 if (m_bThreadRunning == FALSE)
522 if (m_bShowInfo)
524 m_bVisible = TRUE;
525 ShowWindow(SW_SHOWNORMAL);
527 else
529 EndDialog(0);
533 CStandAloneDialog::OnTimer(nIDEvent);
536 void CCheckForUpdatesDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos)
538 CStandAloneDialog::OnWindowPosChanging(lpwndpos);
539 if (m_bVisible == FALSE)
540 lpwndpos->flags &= ~SWP_SHOWWINDOW;
543 BOOL CCheckForUpdatesDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
545 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
546 SetCursor(hCur);
547 return CStandAloneDialogTmpl<CDialog>::OnSetCursor(pWnd, nHitTest, message);
550 void CCheckForUpdatesDlg::OnBnClickedButtonUpdate()
552 CString title;
553 m_ctrlUpdate.GetWindowText(title);
554 if (m_pDownloadThread == NULL && title == CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD)))
556 bool isOneSelected = false;
557 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
559 if (m_ctrlFiles.GetCheck(i))
561 isOneSelected = true;
562 break;
565 if (!isOneSelected)
566 return;
568 m_eventStop.ResetEvent();
570 m_pDownloadThread = ::AfxBeginThread(DownloadThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
571 if (m_pDownloadThread != NULL)
573 m_pDownloadThread->m_bAutoDelete = FALSE;
574 m_pDownloadThread->ResumeThread();
576 GetDlgItem(IDC_BUTTON_UPDATE)->SetWindowText(CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)));
578 else
580 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
583 else if (title == CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)))
585 // Abort
586 m_eventStop.SetEvent();
588 else
590 CString folder = GetDownloadsDirectory();
591 if (m_ctrlUpdate.GetCurrentEntry() == 0)
593 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
595 CUpdateListCtrl::Entry *data = (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
596 if (m_ctrlFiles.GetCheck(i) == TRUE)
597 ShellExecute(NULL, _T("open"), folder + data->m_filename, NULL, NULL, SW_SHOWNORMAL);
599 CStandAloneDialog::OnOK();
601 else if (m_ctrlUpdate.GetCurrentEntry() == 1)
603 ShellExecute(NULL, _T("open"), folder, NULL, NULL, SW_SHOWNORMAL);
606 m_ctrlUpdate.SetCurrentEntry(0);
610 UINT CCheckForUpdatesDlg::DownloadThreadEntry(LPVOID pVoid)
612 return ((CCheckForUpdatesDlg*)pVoid)->DownloadThread();
615 bool CCheckForUpdatesDlg::Download(CString filename)
617 CString url = m_sFilesURL + filename;
618 CString destFilename = GetDownloadsDirectory() + filename;
619 if (PathFileExists(destFilename) && PathFileExists(destFilename + SIGNATURE_FILE_ENDING))
621 if (VerifyIntegrity(destFilename, destFilename + SIGNATURE_FILE_ENDING, m_updateDownloader) == 0)
622 return true;
623 else
625 DeleteFile(destFilename);
626 DeleteFile(destFilename + SIGNATURE_FILE_ENDING);
627 DeleteUrlCacheEntry(url);
628 DeleteUrlCacheEntry(url + SIGNATURE_FILE_ENDING);
632 m_progress.SetRange32(0, 1);
633 m_progress.SetPos(0);
634 m_progress.ShowWindow(SW_SHOW);
635 if (m_pTaskbarList)
637 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
638 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 1);
641 CString tempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
642 CString signatureTempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
643 BOOL ret = m_updateDownloader->DownloadFile(url, tempfile, true);
644 if (!ret)
646 ret = m_updateDownloader->DownloadFile(url + SIGNATURE_FILE_ENDING, signatureTempfile, true);
647 m_progress.SetPos(m_progress.GetPos() + 1);
648 if (m_pTaskbarList)
650 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
651 int minValue, maxValue;
652 m_progress.GetRange(minValue, maxValue);
653 m_pTaskbarList->SetProgressValue(m_hWnd, m_progress.GetPos(), maxValue);
656 if (!ret)
658 if (VerifyIntegrity(tempfile, signatureTempfile, m_updateDownloader) == 0)
660 DeleteFile(destFilename);
661 DeleteFile(destFilename + SIGNATURE_FILE_ENDING);
662 MoveFile(tempfile, destFilename);
663 MoveFile(signatureTempfile, destFilename + SIGNATURE_FILE_ENDING);
664 return true;
666 DeleteUrlCacheEntry(url);
667 DeleteUrlCacheEntry(url + SIGNATURE_FILE_ENDING);
669 return false;
672 UINT CCheckForUpdatesDlg::DownloadThread()
674 m_ctrlFiles.SetExtendedStyle(m_ctrlFiles.GetExtendedStyle() & ~LVS_EX_CHECKBOXES);
676 BOOL result = TRUE;
677 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
679 m_ctrlFiles.EnsureVisible(i, FALSE);
680 CRect rect;
681 m_ctrlFiles.GetItemRect(i, &rect, LVIR_BOUNDS);
682 CUpdateListCtrl::Entry *data = (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
683 if (m_ctrlFiles.GetCheck(i) == TRUE)
685 data->m_status = CUpdateListCtrl::STATUS_DOWNLOADING;
686 m_ctrlFiles.InvalidateRect(rect);
687 if (Download(data->m_filename))
688 data->m_status = CUpdateListCtrl::STATUS_SUCCESS;
689 else
691 data->m_status = CUpdateListCtrl::STATUS_FAIL;
692 result = FALSE;
695 else
696 data->m_status = CUpdateListCtrl::STATUS_IGNORE;
697 m_ctrlFiles.InvalidateRect(rect);
700 ::PostMessage(GetSafeHwnd(), WM_USER_ENDDOWNLOAD, 0, 0);
702 return result;
705 LRESULT CCheckForUpdatesDlg::OnEndDownload(WPARAM, LPARAM)
707 ASSERT(m_pDownloadThread != NULL);
709 // wait until the thread terminates
710 DWORD dwExitCode;
711 if (::GetExitCodeThread(m_pDownloadThread->m_hThread, &dwExitCode) && dwExitCode == STILL_ACTIVE)
712 ::WaitForSingleObject(m_pDownloadThread->m_hThread, INFINITE);
714 // make sure we always have the correct exit code
715 ::GetExitCodeThread(m_pDownloadThread->m_hThread, &dwExitCode);
717 delete m_pDownloadThread;
718 m_pDownloadThread = NULL;
720 m_progress.ShowWindow(SW_HIDE);
722 if (dwExitCode == TRUE)
724 m_ctrlUpdate.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_INSTALL)));
725 m_ctrlUpdate.AddEntry(CString(MAKEINTRESOURCE(IDS_CHECKUPDATE_DESTFOLDER)));
726 m_ctrlUpdate.Invalidate();
727 if (m_pTaskbarList)
728 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
730 else
732 m_ctrlUpdate.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD)));
733 if (m_pTaskbarList)
734 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
735 CMessageBox::Show(NULL, IDS_ERR_FAILEDUPDATEDOWNLOAD, IDS_APPNAME, MB_ICONERROR);
736 if (m_pTaskbarList)
737 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
740 return 0;
743 LRESULT CCheckForUpdatesDlg::OnFillChangelog(WPARAM, LPARAM lParam)
745 ASSERT(lParam != NULL);
747 LPCTSTR changelog = reinterpret_cast<LPCTSTR>(lParam);
748 m_cLogMessage.Call(SCI_SETREADONLY, FALSE);
749 m_cLogMessage.SetText(changelog);
750 m_cLogMessage.Call(SCI_SETREADONLY, TRUE);
751 m_cLogMessage.Call(SCI_GOTOPOS, 0);
753 return 0;
756 CString CCheckForUpdatesDlg::GetDownloadsDirectory()
758 CString folder;
760 if (SysInfo::Instance().IsVistaOrLater())
762 CAutoLibrary hShell = AtlLoadSystemLibraryUsingFullPath(_T("shell32.dll"));
763 if (hShell)
765 typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPathFN(__in REFKNOWNFOLDERID rfid, __in DWORD dwFlags, __in_opt HANDLE hToken, __deref_out PWSTR *ppszPath);
766 SHGetKnownFolderPathFN *pfnSHGetKnownFolderPath = (SHGetKnownFolderPathFN*)GetProcAddress(hShell, "SHGetKnownFolderPath");
767 if (pfnSHGetKnownFolderPath)
769 wchar_t * wcharPtr = 0;
770 HRESULT hr = pfnSHGetKnownFolderPath(FOLDERID_Downloads, KF_FLAG_CREATE, NULL, &wcharPtr);
771 if (SUCCEEDED(hr))
773 folder = wcharPtr;
774 CoTaskMemFree(static_cast<void*>(wcharPtr));
775 return folder.TrimRight(_T("\\")) + _T("\\");
781 TCHAR szPath[MAX_PATH] = {0};
782 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szPath)))
783 folder = szPath;
784 CString downloads = folder.TrimRight(_T("\\")) + _T("\\Downloads\\");
785 if ((PathFileExists(downloads) && PathIsDirectory(downloads)) || (!PathFileExists(downloads) && CreateDirectory(downloads, NULL)))
786 return downloads;
788 return folder;
791 LRESULT CCheckForUpdatesDlg::OnDisplayStatus(WPARAM, LPARAM lParam)
793 const CUpdateDownloader::DOWNLOADSTATUS *const pDownloadStatus = reinterpret_cast<CUpdateDownloader::DOWNLOADSTATUS *>(lParam);
794 if (pDownloadStatus != NULL)
796 ASSERT(::AfxIsValidAddress(pDownloadStatus, sizeof(CUpdateDownloader::DOWNLOADSTATUS)));
798 m_progress.SetRange32(0, pDownloadStatus->ulProgressMax);
799 m_progress.SetPos(pDownloadStatus->ulProgress);
800 if (m_pTaskbarList)
802 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
803 m_pTaskbarList->SetProgressValue(m_hWnd, pDownloadStatus->ulProgress, pDownloadStatus->ulProgressMax);
807 return 0;
810 LRESULT CCheckForUpdatesDlg::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
812 m_pTaskbarList.Release();
813 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
814 return 0;