UpdateCheck: Prompt for offline mode enabled
[TortoiseGit.git] / src / TortoiseProc / CheckForUpdatesDlg.cpp
blob7833587787f5630391153aa200f2788a547f08c2
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2008 - TortoiseSVN
4 // Copyright (C) 2008-2013 - 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"
36 #define SIGNATURE_FILE_ENDING _T(".asc")
38 #define WM_USER_DISPLAYSTATUS (WM_USER + 1)
39 #define WM_USER_ENDDOWNLOAD (WM_USER + 2)
40 #define WM_USER_FILLCHANGELOG (WM_USER + 3)
42 IMPLEMENT_DYNAMIC(CCheckForUpdatesDlg, CStandAloneDialog)
43 CCheckForUpdatesDlg::CCheckForUpdatesDlg(CWnd* pParent /*=NULL*/)
44 : CStandAloneDialog(CCheckForUpdatesDlg::IDD, pParent)
45 , m_bShowInfo(FALSE)
46 , m_bForce(FALSE)
47 , m_bVisible(FALSE)
48 , m_pDownloadThread(NULL)
49 , m_bThreadRunning(FALSE)
51 m_sUpdateDownloadLink = _T("http://redir.tortoisegit.org/download");
54 CCheckForUpdatesDlg::~CCheckForUpdatesDlg()
58 void CCheckForUpdatesDlg::DoDataExchange(CDataExchange* pDX)
60 CStandAloneDialog::DoDataExchange(pDX);
61 DDX_Control(pDX, IDC_LINK, m_link);
62 DDX_Control(pDX, IDC_PROGRESSBAR, m_progress);
63 DDX_Control(pDX, IDC_LIST_DOWNLOADS, m_ctrlFiles);
64 DDX_Control(pDX, IDC_BUTTON_UPDATE, m_ctrlUpdate);
65 DDX_Control(pDX, IDC_LOGMESSAGE, m_cLogMessage);
68 BEGIN_MESSAGE_MAP(CCheckForUpdatesDlg, CStandAloneDialog)
69 ON_WM_TIMER()
70 ON_WM_WINDOWPOSCHANGING()
71 ON_WM_SETCURSOR()
72 ON_WM_DESTROY()
73 ON_BN_CLICKED(IDC_BUTTON_UPDATE, OnBnClickedButtonUpdate)
74 ON_MESSAGE(WM_USER_DISPLAYSTATUS, OnDisplayStatus)
75 ON_MESSAGE(WM_USER_ENDDOWNLOAD, OnEndDownload)
76 ON_MESSAGE(WM_USER_FILLCHANGELOG, OnFillChangelog)
77 END_MESSAGE_MAP()
79 BOOL CCheckForUpdatesDlg::OnInitDialog()
81 CStandAloneDialog::OnInitDialog();
82 CAppUtils::MarkWindowAsUnpinnable(m_hWnd);
84 CString temp;
85 temp.Format(IDS_CHECKNEWER_YOURVERSION, TGIT_VERMAJOR, TGIT_VERMINOR, TGIT_VERMICRO, TGIT_VERBUILD);
86 SetDlgItemText(IDC_YOURVERSION, temp);
88 DialogEnableWindow(IDOK, FALSE);
90 // hide download controls
91 m_ctrlFiles.ShowWindow(SW_HIDE);
92 GetDlgItem(IDC_GROUP_DOWNLOADS)->ShowWindow(SW_HIDE);
93 RECT rectWindow, rectGroupDownloads, rectOKButton;
94 GetWindowRect(&rectWindow);
95 GetDlgItem(IDC_GROUP_DOWNLOADS)->GetWindowRect(&rectGroupDownloads);
96 GetDlgItem(IDOK)->GetWindowRect(&rectOKButton);
97 LONG bottomDistance = rectWindow.bottom - rectOKButton.bottom;
98 OffsetRect(&rectOKButton, 0, rectGroupDownloads.top - rectOKButton.top);
99 rectWindow.bottom = rectOKButton.bottom + bottomDistance;
100 MoveWindow(&rectWindow);
101 ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT)&rectOKButton, 2);
102 GetDlgItem(IDOK)->MoveWindow(&rectOKButton);
104 temp.LoadString(IDS_STATUSLIST_COLFILE);
105 m_ctrlFiles.InsertColumn(0, temp, 0, -1);
106 m_ctrlFiles.InsertColumn(1, temp, 0, -1);
107 m_ctrlFiles.SetExtendedStyle(LVS_EX_DOUBLEBUFFER | LVS_EX_CHECKBOXES);
108 m_ctrlFiles.SetColumnWidth(0, 350);
109 m_ctrlFiles.SetColumnWidth(1, 200);
111 ProjectProperties pp;
112 pp.SetCheckRe(_T("[Ii]ssues?:?(\\s*(,|and)?\\s*#?\\d+)+"));
113 pp.SetBugIDRe(_T("(\\d+)"));
114 pp.lProjectLanguage = -1;
115 pp.sUrl = _T("http://code.google.com/p/tortoisegit/issues/detail?id=%BUGID%");
116 m_cLogMessage.Init(pp);
117 m_cLogMessage.SetFont((CString)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
118 m_cLogMessage.Call(SCI_SETREADONLY, TRUE);
120 if (AfxBeginThread(CheckThreadEntry, this)==NULL)
122 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
125 SetTimer(100, 1000, NULL);
126 return TRUE;
129 void CCheckForUpdatesDlg::OnDestroy()
131 for (int i = 0; i < m_ctrlFiles.GetItemCount(); ++i)
132 delete (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
134 CStandAloneDialog::OnDestroy();
137 void CCheckForUpdatesDlg::OnOK()
139 if (m_bThreadRunning || m_pDownloadThread != NULL)
140 return; // Don't exit while downloading
142 CStandAloneDialog::OnOK();
145 void CCheckForUpdatesDlg::OnCancel()
147 if (m_bThreadRunning || m_pDownloadThread != NULL)
148 return; // Don't exit while downloading
149 CStandAloneDialog::OnCancel();
152 UINT CCheckForUpdatesDlg::CheckThreadEntry(LPVOID pVoid)
154 return ((CCheckForUpdatesDlg*)pVoid)->CheckThread();
157 UINT CCheckForUpdatesDlg::CheckThread()
159 m_bThreadRunning = TRUE;
161 CString temp;
162 CString tempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
164 bool official = false;
166 CRegString checkurluser = CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""));
167 CRegString checkurlmachine = CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""), FALSE, HKEY_LOCAL_MACHINE);
168 CString sCheckURL = checkurluser;
169 if (sCheckURL.IsEmpty())
171 sCheckURL = checkurlmachine;
172 if (sCheckURL.IsEmpty())
174 official = true;
175 bool checkPreview = false;
176 #if PREVIEW
177 checkPreview = true;
178 #else
179 CRegStdDWORD regCheckPreview(L"Software\\TortoiseGit\\VersionCheckPreview", FALSE);
180 if (DWORD(regCheckPreview))
181 checkPreview = true;
182 #endif
183 if (checkPreview)
185 sCheckURL = _T("http://version.tortoisegit.googlecode.com/git/version-preview.txt");
186 SetDlgItemText(IDC_SOURCE, _T("Using preview release channel"));
188 else
189 sCheckURL = _T("http://version.tortoisegit.googlecode.com/git/version.txt");
193 if (!official)
194 SetDlgItemText(IDC_SOURCE, _T("Using (unofficial) release channel: ") + sCheckURL);
196 CoInitialize(NULL);
197 CString errorText;
198 if (m_bForce)
199 DeleteUrlCacheEntry(sCheckURL);
200 HRESULT res = URLDownloadToFile(NULL, sCheckURL, tempfile, 0, NULL);
201 if (res == S_OK && official)
203 CString signatureTempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
204 if (m_bForce)
205 DeleteUrlCacheEntry(sCheckURL + SIGNATURE_FILE_ENDING);
206 res = URLDownloadToFile(nullptr, sCheckURL + SIGNATURE_FILE_ENDING, signatureTempfile, 0, nullptr);
207 if (res == S_OK && VerifyIntegrity(tempfile, signatureTempfile))
209 SetDlgItemText(IDC_CHECKRESULT, _T("Could not verify digital signature."));
210 DeleteUrlCacheEntry(sCheckURL);
211 DeleteUrlCacheEntry(sCheckURL + SIGNATURE_FILE_ENDING);
212 goto finish;
215 else if (FAILED(res))
217 DeleteUrlCacheEntry(sCheckURL);
218 if (CRegDWORD(_T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\GlobalUserOffline"), 0))
219 errorText.LoadString(IDS_OFFLINEMODE); // offline mode enabled
220 else
221 errorText.Format(IDS_CHECKNEWER_NETERROR_FORMAT, res);
223 if (res == S_OK)
227 CStdioFile file(tempfile, CFile::modeRead | CFile::shareDenyWrite);
228 CString ver;
229 unsigned int major,minor,micro,build;
230 major=minor=micro=build=0;
231 unsigned __int64 version=0;
233 if (file.ReadString(ver))
235 CString vertemp = ver;
236 // another versionstring for the filename can be provided after a semicolon
237 // this is needed for preview releases
238 int differentFilenamePos = vertemp.Find(_T(";"));
239 if (differentFilenamePos > 0)
241 vertemp = vertemp.Left(differentFilenamePos);
242 ver = ver.Mid(differentFilenamePos + 1);
245 major = _ttoi(vertemp);
246 vertemp = vertemp.Mid(vertemp.Find('.')+1);
247 minor = _ttoi(vertemp);
248 vertemp = vertemp.Mid(vertemp.Find('.')+1);
249 micro = _ttoi(vertemp);
250 vertemp = vertemp.Mid(vertemp.Find('.')+1);
251 build = _ttoi(vertemp);
252 version = major;
253 version <<= 16;
254 version += minor;
255 version <<= 16;
256 version += micro;
257 version <<= 16;
258 version += build;
262 BOOL bNewer = FALSE;
263 if (m_bForce)
264 bNewer = TRUE;
265 if (major > TGIT_VERMAJOR)
266 bNewer = TRUE;
267 else if ((minor > TGIT_VERMINOR)&&(major == TGIT_VERMAJOR))
268 bNewer = TRUE;
269 else if ((micro > TGIT_VERMICRO)&&(minor == TGIT_VERMINOR)&&(major == TGIT_VERMAJOR))
270 bNewer = TRUE;
271 else if ((build > TGIT_VERBUILD)&&(micro == TGIT_VERMICRO)&&(minor == TGIT_VERMINOR)&&(major == TGIT_VERMAJOR))
272 bNewer = TRUE;
274 if (version != 0)
276 CString version;
277 version.Format(_T("%u.%u.%u.%u"),major,minor,micro,build);
278 if (version != ver)
279 version += _T(" (") + ver + _T(")");
280 temp.Format(IDS_CHECKNEWER_CURRENTVERSION, (LPCTSTR)version);
281 SetDlgItemText(IDC_CURRENTVERSION, temp);
284 if (version == 0)
286 temp.LoadString(IDS_CHECKNEWER_NETERROR);
287 SetDlgItemText(IDC_CHECKRESULT, temp);
289 else if (bNewer)
291 if(file.ReadString(temp) && !temp.IsEmpty())
292 { // Read the next line, it could contain a message for the user
293 CString tempLink;
294 if(file.ReadString(tempLink) && !tempLink.IsEmpty())
295 { // Read another line to find out the download link-URL, if any
296 m_sUpdateDownloadLink = tempLink;
299 else
301 temp.LoadString(IDS_CHECKNEWER_NEWERVERSIONAVAILABLE);
302 CString tempLink;
303 file.ReadString(tempLink);
305 SetDlgItemText(IDC_CHECKRESULT, temp);
306 m_bShowInfo = TRUE;
308 FillChangelog(file);
309 FillDownloads(file, ver);
311 // Show download controls
312 RECT rectWindow, rectProgress, rectGroupDownloads, rectOKButton;
313 GetWindowRect(&rectWindow);
314 m_progress.GetWindowRect(&rectProgress);
315 GetDlgItem(IDC_GROUP_DOWNLOADS)->GetWindowRect(&rectGroupDownloads);
316 GetDlgItem(IDOK)->GetWindowRect(&rectOKButton);
317 LONG bottomDistance = rectWindow.bottom - rectOKButton.bottom;
318 OffsetRect(&rectOKButton, 0, (rectGroupDownloads.bottom + (rectGroupDownloads.bottom - rectProgress.bottom)) - rectOKButton.top);
319 rectWindow.bottom = rectOKButton.bottom + bottomDistance;
320 MoveWindow(&rectWindow);
321 ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT)&rectOKButton, 2);
322 GetDlgItem(IDOK)->MoveWindow(&rectOKButton);
323 m_ctrlFiles.ShowWindow(SW_SHOW);
324 GetDlgItem(IDC_GROUP_DOWNLOADS)->ShowWindow(SW_SHOW);
325 CenterWindow();
327 else
329 temp.LoadString(IDS_CHECKNEWER_YOURUPTODATE);
330 SetDlgItemText(IDC_CHECKRESULT, temp);
331 file.ReadString(temp);
332 file.ReadString(temp);
333 FillChangelog(file);
337 catch (CException * e)
339 e->Delete();
340 temp.LoadString(IDS_CHECKNEWER_NETERROR);
341 SetDlgItemText(IDC_CHECKRESULT, temp);
344 else
346 SetDlgItemText(IDC_CHECKRESULT, errorText);
348 if (!m_sUpdateDownloadLink.IsEmpty())
350 m_link.ShowWindow(SW_SHOW);
351 m_link.SetURL(m_sUpdateDownloadLink);
354 finish:
355 CoUninitialize();
356 m_bThreadRunning = FALSE;
357 DialogEnableWindow(IDOK, TRUE);
358 return 0;
361 void CCheckForUpdatesDlg::FillDownloads(CStdioFile &file, CString version)
363 #if WIN64
364 const CString x86x64 = _T("64");
365 #else
366 const CString x86x64 = _T("32");
367 #endif
369 if (!file.ReadString(m_sFilesURL) || m_sFilesURL.IsEmpty())
370 m_sFilesURL = _T("http://tortoisegit.googlecode.com/files/");
372 m_ctrlFiles.InsertItem(0, _T("TortoiseGit"));
373 CString filename;
374 filename.Format(_T("TortoiseGit-%s-%sbit.msi"), version, x86x64);
375 m_ctrlFiles.SetItemData(0, (DWORD_PTR)(new CUpdateListCtrl::Entry(filename, CUpdateListCtrl::STATUS_NONE)));
376 m_ctrlFiles.SetCheck(0 , TRUE);
378 std::vector<DWORD> m_installedLangs;
380 // set up the language selecting combobox
381 CString path = CPathUtils::GetAppParentDirectory();
382 path = path + _T("Languages\\");
383 CSimpleFileFind finder(path, _T("*.dll"));
384 while (finder.FindNextFileNoDirectories())
386 CString file = finder.GetFilePath();
387 CString filename = finder.GetFileName();
388 if (filename.Left(12).CompareNoCase(_T("TortoiseProc")) == 0)
390 CString sVer = _T(STRPRODUCTVER);
391 sVer = sVer.Left(sVer.ReverseFind('.'));
392 CString sFileVer = CPathUtils::GetVersionFromFile(file);
393 sFileVer = sFileVer.Left(sFileVer.ReverseFind('.'));
394 CString sLoc = filename.Mid(12);
395 sLoc = sLoc.Left(sLoc.GetLength() - 4); // cut off ".dll"
396 if ((sLoc.Left(2) == L"32") && (sLoc.GetLength() > 5))
397 continue;
398 DWORD loc = _tstoi(filename.Mid(12));
399 m_installedLangs.push_back(loc);
404 CString langs;
405 while (file.ReadString(langs) && !langs.IsEmpty())
407 CString sLang = _T("TortoiseGit Language Pack ") + langs.Mid(5);
409 DWORD loc = _tstoi(langs.Mid(0, 4));
410 TCHAR buf[MAX_PATH];
411 GetLocaleInfo(loc, LOCALE_SNATIVELANGNAME, buf, _countof(buf));
412 CString sLang2(buf);
413 GetLocaleInfo(loc, LOCALE_SNATIVECTRYNAME, buf, _countof(buf));
414 if (buf[0])
416 sLang2 += _T(" (");
417 sLang2 += buf;
418 sLang2 += _T(")");
421 int pos = m_ctrlFiles.InsertItem(m_ctrlFiles.GetItemCount(), sLang);
422 m_ctrlFiles.SetItemText(pos, 1, sLang2);
424 CString filename;
425 filename.Format(_T("TortoiseGit-LanguagePack-%s-%sbit-%s.msi"), version, x86x64, langs.Mid(5));
426 m_ctrlFiles.SetItemData(pos, (DWORD_PTR)(new CUpdateListCtrl::Entry(filename, CUpdateListCtrl::STATUS_NONE)));
428 if (std::find(m_installedLangs.begin(), m_installedLangs.end(), loc) != m_installedLangs.end())
429 m_ctrlFiles.SetCheck(pos , TRUE);
431 DialogEnableWindow(IDC_BUTTON_UPDATE, TRUE);
434 void CCheckForUpdatesDlg::FillChangelog(CStdioFile &file)
436 CString sChangelogURL;
437 if (!file.ReadString(sChangelogURL) || sChangelogURL.IsEmpty())
438 sChangelogURL = _T("http://tortoisegit.googlecode.com/git/src/Changelog.txt");
440 CString tempchangelogfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
441 HRESULT res = URLDownloadToFile(NULL, sChangelogURL, tempchangelogfile, 0, NULL);
442 if (SUCCEEDED(res))
444 CString temp;
445 CStdioFile file(tempchangelogfile, CFile::modeRead|CFile::typeText);
446 CString str;
447 bool first = true;
448 while (file.ReadString(str))
450 if (first)
452 first = false;
453 if (str.GetLength() > 2 && str.GetAt(0) == 0xEF && str.GetAt(1) == 0xBB)
455 if (str.GetAt(2) == 0xBF)
457 str = str.Mid(3);
459 else
461 str = str.Mid(2);
465 str = CUnicodeUtils::GetUnicode(CStringA(str), CP_UTF8);
466 temp += str + _T("\n");
468 ::SendMessage(m_hWnd, WM_USER_FILLCHANGELOG, 0, reinterpret_cast<LPARAM>(temp.GetBuffer()));
470 else
471 ::SendMessage(m_hWnd, WM_USER_FILLCHANGELOG, 0, reinterpret_cast<LPARAM>(_T("Could not load changelog.")));
474 void CCheckForUpdatesDlg::OnTimer(UINT_PTR nIDEvent)
476 if (nIDEvent == 100)
478 if (m_bThreadRunning == FALSE)
480 if (m_bShowInfo)
482 m_bVisible = TRUE;
483 ShowWindow(SW_SHOWNORMAL);
485 else
487 EndDialog(0);
491 CStandAloneDialog::OnTimer(nIDEvent);
494 void CCheckForUpdatesDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos)
496 CStandAloneDialog::OnWindowPosChanging(lpwndpos);
497 if (m_bVisible == FALSE)
498 lpwndpos->flags &= ~SWP_SHOWWINDOW;
501 BOOL CCheckForUpdatesDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
503 HCURSOR hCur = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
504 SetCursor(hCur);
505 return CStandAloneDialogTmpl<CDialog>::OnSetCursor(pWnd, nHitTest, message);
508 void CCheckForUpdatesDlg::OnBnClickedButtonUpdate()
510 CString title;
511 m_ctrlUpdate.GetWindowText(title);
512 if (m_pDownloadThread == NULL && title == CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD)))
514 bool isOneSelected = false;
515 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
517 if (m_ctrlFiles.GetCheck(i))
519 isOneSelected = true;
520 break;
523 if (!isOneSelected)
524 return;
526 m_eventStop.ResetEvent();
528 m_pDownloadThread = ::AfxBeginThread(DownloadThreadEntry, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
529 if (m_pDownloadThread != NULL)
531 m_pDownloadThread->m_bAutoDelete = FALSE;
532 m_pDownloadThread->ResumeThread();
534 GetDlgItem(IDC_BUTTON_UPDATE)->SetWindowText(CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)));
536 else
538 CMessageBox::Show(NULL, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
541 else if (title == CString(MAKEINTRESOURCE(IDS_ABORTBUTTON)))
543 // Abort
544 m_eventStop.SetEvent();
546 else
548 CString folder = GetDownloadsDirectory();
549 if (m_ctrlUpdate.GetCurrentEntry() == 0)
551 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
553 CUpdateListCtrl::Entry *data = (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
554 if (m_ctrlFiles.GetCheck(i) == TRUE)
555 ShellExecute(NULL, _T("open"), folder + data->m_filename, NULL, NULL, SW_SHOWNORMAL);
557 CStandAloneDialog::OnOK();
559 else if (m_ctrlUpdate.GetCurrentEntry() == 1)
561 ShellExecute(NULL, _T("open"), folder, NULL, NULL, SW_SHOWNORMAL);
564 m_ctrlUpdate.SetCurrentEntry(0);
568 UINT CCheckForUpdatesDlg::DownloadThreadEntry(LPVOID pVoid)
570 return ((CCheckForUpdatesDlg*)pVoid)->DownloadThread();
573 bool CCheckForUpdatesDlg::Download(CString filename)
575 CString url = m_sFilesURL + filename;
576 CString destFilename = GetDownloadsDirectory() + filename;
577 if (PathFileExists(destFilename) && PathFileExists(destFilename + SIGNATURE_FILE_ENDING))
579 if (VerifyIntegrity(destFilename, destFilename + SIGNATURE_FILE_ENDING) == 0)
580 return true;
581 else
583 DeleteFile(destFilename);
584 DeleteFile(destFilename + SIGNATURE_FILE_ENDING);
585 DeleteUrlCacheEntry(url);
586 DeleteUrlCacheEntry(url + SIGNATURE_FILE_ENDING);
590 CBSCallbackImpl bsc(this->GetSafeHwnd(), m_eventStop);
592 m_progress.SetRange32(0, 1);
593 m_progress.SetPos(0);
594 m_progress.ShowWindow(SW_SHOW);
596 CString tempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
597 CString signatureTempfile = CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
598 if (m_bForce)
599 DeleteUrlCacheEntry(url);
600 HRESULT res = URLDownloadToFile(NULL, url, tempfile, 0, &bsc);
601 if (res == S_OK)
603 if (m_bForce)
604 DeleteUrlCacheEntry(url + SIGNATURE_FILE_ENDING);
605 res = URLDownloadToFile(NULL, url + SIGNATURE_FILE_ENDING, signatureTempfile, 0, nullptr);
606 m_progress.SetPos(m_progress.GetPos() + 1);
608 if (res == S_OK)
610 if (VerifyIntegrity(tempfile, signatureTempfile) == 0)
612 DeleteFile(destFilename);
613 DeleteFile(destFilename + SIGNATURE_FILE_ENDING);
614 MoveFile(tempfile, destFilename);
615 MoveFile(signatureTempfile, destFilename + SIGNATURE_FILE_ENDING);
616 return true;
618 DeleteUrlCacheEntry(url);
619 DeleteUrlCacheEntry(url + SIGNATURE_FILE_ENDING);
621 return false;
624 UINT CCheckForUpdatesDlg::DownloadThread()
626 m_ctrlFiles.SetExtendedStyle(m_ctrlFiles.GetExtendedStyle() & ~LVS_EX_CHECKBOXES);
628 CoInitialize(NULL);
630 BOOL result = TRUE;
631 for (int i = 0; i < (int)m_ctrlFiles.GetItemCount(); ++i)
633 m_ctrlFiles.EnsureVisible(i, FALSE);
634 CRect rect;
635 m_ctrlFiles.GetItemRect(i, &rect, LVIR_BOUNDS);
636 CUpdateListCtrl::Entry *data = (CUpdateListCtrl::Entry *)m_ctrlFiles.GetItemData(i);
637 if (m_ctrlFiles.GetCheck(i) == TRUE)
639 data->m_status = CUpdateListCtrl::STATUS_DOWNLOADING;
640 m_ctrlFiles.InvalidateRect(rect);
641 if (Download(data->m_filename))
642 data->m_status = CUpdateListCtrl::STATUS_SUCCESS;
643 else
645 data->m_status = CUpdateListCtrl::STATUS_FAIL;
646 result = FALSE;
649 else
650 data->m_status = CUpdateListCtrl::STATUS_IGNORE;
651 m_ctrlFiles.InvalidateRect(rect);
654 ::PostMessage(GetSafeHwnd(), WM_USER_ENDDOWNLOAD, 0, 0);
656 CoUninitialize();
658 return result;
661 LRESULT CCheckForUpdatesDlg::OnEndDownload(WPARAM, LPARAM)
663 ASSERT(m_pDownloadThread != NULL);
665 // wait until the thread terminates
666 DWORD dwExitCode;
667 if (::GetExitCodeThread(m_pDownloadThread->m_hThread, &dwExitCode) && dwExitCode == STILL_ACTIVE)
668 ::WaitForSingleObject(m_pDownloadThread->m_hThread, INFINITE);
670 // make sure we always have the correct exit code
671 ::GetExitCodeThread(m_pDownloadThread->m_hThread, &dwExitCode);
673 delete m_pDownloadThread;
674 m_pDownloadThread = NULL;
676 m_progress.ShowWindow(SW_HIDE);
678 if (dwExitCode == TRUE)
680 m_ctrlUpdate.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_INSTALL)));
681 m_ctrlUpdate.AddEntry(CString(MAKEINTRESOURCE(IDS_CHECKUPDATE_DESTFOLDER)));
682 m_ctrlUpdate.Invalidate();
684 else
686 m_ctrlUpdate.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD)));
687 CMessageBox::Show(NULL, IDS_ERR_FAILEDUPDATEDOWNLOAD, IDS_APPNAME, MB_ICONERROR);
690 return 0;
693 LRESULT CCheckForUpdatesDlg::OnFillChangelog(WPARAM, LPARAM lParam)
695 ASSERT(lParam != NULL);
697 TCHAR * changelog = reinterpret_cast<TCHAR *>(lParam);
698 m_cLogMessage.Call(SCI_SETREADONLY, FALSE);
699 m_cLogMessage.SetText(changelog);
700 m_cLogMessage.Call(SCI_SETREADONLY, TRUE);
701 m_cLogMessage.Call(SCI_GOTOPOS, 0);
703 return 0;
706 CString CCheckForUpdatesDlg::GetDownloadsDirectory()
708 CString folder;
710 if (SysInfo::Instance().IsVistaOrLater())
712 CAutoLibrary hShell = AtlLoadSystemLibraryUsingFullPath(_T("shell32.dll"));
713 if (hShell)
715 typedef HRESULT STDAPICALLTYPE SHGetKnownFolderPathFN(__in REFKNOWNFOLDERID rfid, __in DWORD dwFlags, __in_opt HANDLE hToken, __deref_out PWSTR *ppszPath);
716 SHGetKnownFolderPathFN *pfnSHGetKnownFolderPath = (SHGetKnownFolderPathFN*)GetProcAddress(hShell, "SHGetKnownFolderPath");
717 if (pfnSHGetKnownFolderPath)
719 wchar_t * wcharPtr = 0;
720 HRESULT hr = pfnSHGetKnownFolderPath(FOLDERID_Downloads, KF_FLAG_CREATE, NULL, &wcharPtr);
721 if (SUCCEEDED(hr))
723 folder = wcharPtr;
724 CoTaskMemFree(static_cast<void*>(wcharPtr));
725 return folder.TrimRight(_T("\\")) + _T("\\");
731 TCHAR szPath[MAX_PATH];
732 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szPath)))
733 folder = szPath;
734 CString downloads = folder.TrimRight(_T("\\")) + _T("\\Downloads\\");
735 if ((PathFileExists(downloads) && PathIsDirectory(downloads)) || (!PathFileExists(downloads) && CreateDirectory(downloads, NULL)))
736 return downloads;
738 return folder;
741 LRESULT CCheckForUpdatesDlg::OnDisplayStatus(WPARAM, LPARAM lParam)
743 const DOWNLOADSTATUS *const pDownloadStatus = reinterpret_cast<DOWNLOADSTATUS *>(lParam);
744 if (pDownloadStatus != NULL)
746 ASSERT(::AfxIsValidAddress(pDownloadStatus, sizeof(DOWNLOADSTATUS)));
748 m_progress.SetRange32(0, pDownloadStatus->ulProgressMax);
749 m_progress.SetPos(pDownloadStatus->ulProgress);
752 return 0;
755 CBSCallbackImpl::CBSCallbackImpl(HWND hWnd, HANDLE hEventStop)
757 m_hWnd = hWnd;
758 m_hEventStop = hEventStop;
759 m_ulObjRefCount = 1;
762 // IUnknown
763 STDMETHODIMP CBSCallbackImpl::QueryInterface(REFIID riid, void **ppvObject)
765 *ppvObject = NULL;
767 // IUnknown
768 if (::IsEqualIID(riid, __uuidof(IUnknown)))
769 *ppvObject = this;
771 // IBindStatusCallback
772 else if (::IsEqualIID(riid, __uuidof(IBindStatusCallback)))
773 *ppvObject = static_cast<IBindStatusCallback *>(this);
775 if (*ppvObject)
777 (*reinterpret_cast<LPUNKNOWN *>(ppvObject))->AddRef();
778 return S_OK;
781 return E_NOINTERFACE;
784 STDMETHODIMP_(ULONG) CBSCallbackImpl::AddRef()
786 return ++m_ulObjRefCount;
789 STDMETHODIMP_(ULONG) CBSCallbackImpl::Release()
791 return --m_ulObjRefCount;
794 // IBindStatusCallback
795 STDMETHODIMP CBSCallbackImpl::OnStartBinding(DWORD, IBinding *)
797 return S_OK;
800 STDMETHODIMP CBSCallbackImpl::GetPriority(LONG *)
802 return E_NOTIMPL;
804 STDMETHODIMP CBSCallbackImpl::OnLowResource(DWORD)
806 return S_OK;
809 STDMETHODIMP CBSCallbackImpl::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR /* szStatusText */)
811 TRACE(_T("IBindStatusCallback::OnProgress\n"));
812 TRACE(_T("ulProgress: %lu, ulProgressMax: %lu\n"), ulProgress, ulProgressMax);
813 UNREFERENCED_PARAMETER(ulStatusCode);
814 TRACE(_T("ulStatusCode: %lu "), ulStatusCode);
816 if (m_hWnd != NULL)
818 // inform the dialog box to display current status,
819 // don't use PostMessage
820 CCheckForUpdatesDlg::DOWNLOADSTATUS downloadStatus = { ulProgress, ulProgressMax + 1 }; // + 1 for download of signature file
821 ::SendMessage(m_hWnd, WM_USER_DISPLAYSTATUS, 0, reinterpret_cast<LPARAM>(&downloadStatus));
824 if (m_hEventStop != NULL && ::WaitForSingleObject(m_hEventStop, 0) == WAIT_OBJECT_0)
826 return E_ABORT; // canceled by the user
829 return S_OK;
832 STDMETHODIMP CBSCallbackImpl::OnStopBinding(HRESULT, LPCWSTR)
834 return S_OK;
837 STDMETHODIMP CBSCallbackImpl::GetBindInfo(DWORD *, BINDINFO *)
839 return S_OK;
842 STDMETHODIMP CBSCallbackImpl::OnDataAvailable(DWORD, DWORD, FORMATETC *, STGMEDIUM *)
844 return S_OK;
847 STDMETHODIMP CBSCallbackImpl::OnObjectAvailable(REFIID, IUnknown *)
849 return S_OK;