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.
21 #include "TortoiseProc.h"
22 #include "LoglistCommonResource.h"
23 #include "..\version.h"
24 #include "MessageBox.h"
25 #include "CheckForUpdatesDlg.h"
29 #include "SmartHandle.h"
31 #include "PathUtils.h"
32 #include "DirFileEnum.h"
33 #include "UnicodeUtils.h"
34 #include "UpdateCrypto.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
)
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
)
72 ON_WM_WINDOWPOSCHANGING()
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 ON_BN_CLICKED(IDC_DONOTASKAGAIN
, &CCheckForUpdatesDlg::OnBnClickedDonotaskagain
)
83 BOOL
CCheckForUpdatesDlg::OnInitDialog()
85 CStandAloneDialog::OnInitDialog();
86 CAppUtils::MarkWindowAsUnpinnable(m_hWnd
);
89 temp
.Format(IDS_CHECKNEWER_YOURVERSION
, TGIT_VERMAJOR
, TGIT_VERMINOR
, TGIT_VERMICRO
, TGIT_VERBUILD
);
90 SetDlgItemText(IDC_YOURVERSION
, temp
);
92 DialogEnableWindow(IDOK
, FALSE
);
94 m_pTaskbarList
.Release();
95 if (FAILED(m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
)))
96 m_pTaskbarList
= nullptr;
98 // hide download controls
99 m_ctrlFiles
.ShowWindow(SW_HIDE
);
100 GetDlgItem(IDC_GROUP_DOWNLOADS
)->ShowWindow(SW_HIDE
);
101 RECT rectWindow
, rectGroupDownloads
, rectOKButton
;
102 GetWindowRect(&rectWindow
);
103 GetDlgItem(IDC_GROUP_DOWNLOADS
)->GetWindowRect(&rectGroupDownloads
);
104 GetDlgItem(IDOK
)->GetWindowRect(&rectOKButton
);
105 LONG bottomDistance
= rectWindow
.bottom
- rectOKButton
.bottom
;
106 OffsetRect(&rectOKButton
, 0, rectGroupDownloads
.top
- rectOKButton
.top
);
107 rectWindow
.bottom
= rectOKButton
.bottom
+ bottomDistance
;
108 MoveWindow(&rectWindow
);
109 ::MapWindowPoints(NULL
, GetSafeHwnd(), (LPPOINT
)&rectOKButton
, 2);
110 GetDlgItem(IDOK
)->MoveWindow(&rectOKButton
);
112 temp
.LoadString(IDS_STATUSLIST_COLFILE
);
113 m_ctrlFiles
.InsertColumn(0, temp
, 0, -1);
114 m_ctrlFiles
.InsertColumn(1, temp
, 0, -1);
115 m_ctrlFiles
.SetExtendedStyle(LVS_EX_DOUBLEBUFFER
| LVS_EX_CHECKBOXES
);
116 m_ctrlFiles
.SetColumnWidth(0, 350);
117 m_ctrlFiles
.SetColumnWidth(1, 200);
119 m_cLogMessage
.Init(-1);
120 m_cLogMessage
.SetFont((CString
)CRegString(_T("Software\\TortoiseGit\\LogFontName"), _T("Courier New")), (DWORD
)CRegDWORD(_T("Software\\TortoiseGit\\LogFontSize"), 8));
121 m_cLogMessage
.Call(SCI_SETREADONLY
, TRUE
);
123 m_updateDownloader
= new CUpdateDownloader(GetSafeHwnd(), m_bForce
== TRUE
, WM_USER_DISPLAYSTATUS
, &m_eventStop
);
125 if (AfxBeginThread(CheckThreadEntry
, this)==NULL
)
127 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
130 SetTimer(100, 1000, NULL
);
134 void CCheckForUpdatesDlg::OnDestroy()
136 for (int i
= 0; i
< m_ctrlFiles
.GetItemCount(); ++i
)
137 delete (CUpdateListCtrl::Entry
*)m_ctrlFiles
.GetItemData(i
);
139 if (m_updateDownloader
)
140 delete m_updateDownloader
;
142 CStandAloneDialog::OnDestroy();
145 void CCheckForUpdatesDlg::OnOK()
147 if (m_bThreadRunning
|| m_pDownloadThread
!= NULL
)
148 return; // Don't exit while downloading
150 CStandAloneDialog::OnOK();
153 void CCheckForUpdatesDlg::OnCancel()
155 if (m_bThreadRunning
|| m_pDownloadThread
!= NULL
)
156 return; // Don't exit while downloading
157 CStandAloneDialog::OnCancel();
160 UINT
CCheckForUpdatesDlg::CheckThreadEntry(LPVOID pVoid
)
162 return ((CCheckForUpdatesDlg
*)pVoid
)->CheckThread();
165 UINT
CCheckForUpdatesDlg::CheckThread()
167 m_bThreadRunning
= TRUE
;
170 CString tempfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
172 bool official
= false;
174 CRegString checkurluser
= CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""));
175 CRegString checkurlmachine
= CRegString(_T("Software\\TortoiseGit\\UpdateCheckURL"), _T(""), FALSE
, HKEY_LOCAL_MACHINE
);
176 CString sCheckURL
= checkurluser
;
177 if (sCheckURL
.IsEmpty())
179 sCheckURL
= checkurlmachine
;
180 if (sCheckURL
.IsEmpty())
183 bool checkPreview
= false;
187 CRegStdDWORD
regCheckPreview(L
"Software\\TortoiseGit\\VersionCheckPreview", FALSE
);
188 if (DWORD(regCheckPreview
))
193 sCheckURL
= _T("://versioncheck.tortoisegit.org/version-preview.txt");
194 SetDlgItemText(IDC_SOURCE
, _T("Using preview release channel"));
197 sCheckURL
= _T("://versioncheck.tortoisegit.org/version.txt");
198 if (SysInfo::Instance().IsVistaOrLater()) // we need SNI support
199 sCheckURL
= _T("https") + sCheckURL
;
201 sCheckURL
= _T("http") + sCheckURL
;
205 if (!official
&& sCheckURL
.Find(_T("://versioncheck.tortoisegit.org/")) > 0)
209 SetDlgItemText(IDC_SOURCE
, _T("Using (unofficial) release channel: ") + sCheckURL
);
212 CAutoConfig
versioncheck(true);
214 DWORD ret
= m_updateDownloader
->DownloadFile(sCheckURL
, tempfile
, false);
215 if (!ret
&& official
)
217 CString signatureTempfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
218 ret
= m_updateDownloader
->DownloadFile(sCheckURL
+ SIGNATURE_FILE_ENDING
, signatureTempfile
, false);
219 if (ret
|| VerifyIntegrity(tempfile
, signatureTempfile
, m_updateDownloader
))
221 CString error
= _T("Could not verify digital signature.");
223 error
+= _T("\r\nError: ") + GetWinINetError(ret
) + _T(" (on ") + sCheckURL
+ SIGNATURE_FILE_ENDING
+ _T(")");
224 SetDlgItemText(IDC_CHECKRESULT
, error
);
225 DeleteUrlCacheEntry(sCheckURL
);
226 DeleteUrlCacheEntry(sCheckURL
+ SIGNATURE_FILE_ENDING
);
232 DeleteUrlCacheEntry(sCheckURL
);
233 if (CRegDWORD(_T("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\GlobalUserOffline"), 0))
234 errorText
.LoadString(IDS_OFFLINEMODE
); // offline mode enabled
236 errorText
.Format(IDS_CHECKNEWER_NETERROR_FORMAT
, GetWinINetError(ret
) + _T(" URL: ") + sCheckURL
, ret
);
237 SetDlgItemText(IDC_CHECKRESULT
, errorText
);
241 git_config_add_file_ondisk(versioncheck
, CUnicodeUtils::GetUTF8(tempfile
), GIT_CONFIG_LEVEL_GLOBAL
, 0);
243 unsigned int major
, minor
, micro
, build
;
244 major
= minor
= micro
= build
= 0;
245 unsigned __int64 version
= 0;
247 if (!versioncheck
.GetString(_T("tortoisegit.version"), ver
))
249 CString vertemp
= ver
;
250 major
= _ttoi(vertemp
);
251 vertemp
= vertemp
.Mid(vertemp
.Find('.') + 1);
252 minor
= _ttoi(vertemp
);
253 vertemp
= vertemp
.Mid(vertemp
.Find('.') + 1);
254 micro
= _ttoi(vertemp
);
255 vertemp
= vertemp
.Mid(vertemp
.Find('.') + 1);
256 build
= _ttoi(vertemp
);
267 temp
.LoadString(IDS_CHECKNEWER_NETERROR
);
268 SetDlgItemText(IDC_CHECKRESULT
, temp
);
272 // another versionstring for the filename can be provided
273 // this is needed for preview releases
275 versioncheck
.GetString(_T("tortoisegit.versionstring"), vertemp
);
276 if (!vertemp
.IsEmpty())
281 errorText
= _T("Could not parse version check file: ") + g_Git
.GetLibGit2LastErr();
282 SetDlgItemText(IDC_CHECKRESULT
, errorText
);
283 DeleteUrlCacheEntry(sCheckURL
);
291 else if (major
> TGIT_VERMAJOR
)
293 else if ((minor
> TGIT_VERMINOR
) && (major
== TGIT_VERMAJOR
))
295 else if ((micro
> TGIT_VERMICRO
) && (minor
== TGIT_VERMINOR
) && (major
== TGIT_VERMAJOR
))
297 else if ((build
> TGIT_VERBUILD
) && (micro
== TGIT_VERMICRO
) && (minor
== TGIT_VERMINOR
) && (major
== TGIT_VERMAJOR
))
301 versionstr
.Format(_T("%u.%u.%u.%u"), major
, minor
, micro
, build
);
302 if (versionstr
!= ver
)
303 versionstr
+= _T(" (") + ver
+ _T(")");
304 temp
.Format(IDS_CHECKNEWER_CURRENTVERSION
, (LPCTSTR
)versionstr
);
305 SetDlgItemText(IDC_CURRENTVERSION
, temp
);
309 versioncheck
.GetString(_T("tortoisegit.infotext"), temp
);
313 versioncheck
.GetString(_T("tortoisegit.infotexturl"), tempLink
);
314 if (!tempLink
.IsEmpty()) // find out the download link-URL, if any
315 m_sUpdateDownloadLink
= tempLink
;
318 temp
.LoadString(IDS_CHECKNEWER_NEWERVERSIONAVAILABLE
);
319 SetDlgItemText(IDC_CHECKRESULT
, temp
);
321 FillChangelog(versioncheck
, official
);
322 FillDownloads(versioncheck
, ver
);
324 // Show download controls
325 RECT rectWindow
, rectProgress
, rectGroupDownloads
, rectOKButton
;
326 GetWindowRect(&rectWindow
);
327 m_progress
.GetWindowRect(&rectProgress
);
328 GetDlgItem(IDC_GROUP_DOWNLOADS
)->GetWindowRect(&rectGroupDownloads
);
329 GetDlgItem(IDOK
)->GetWindowRect(&rectOKButton
);
330 LONG bottomDistance
= rectWindow
.bottom
- rectOKButton
.bottom
;
331 OffsetRect(&rectOKButton
, 0, (rectGroupDownloads
.bottom
+ (rectGroupDownloads
.bottom
- rectProgress
.bottom
)) - rectOKButton
.top
);
332 rectWindow
.bottom
= rectOKButton
.bottom
+ bottomDistance
;
333 MoveWindow(&rectWindow
);
334 ::MapWindowPoints(NULL
, GetSafeHwnd(), (LPPOINT
)&rectOKButton
, 2);
335 if (CRegDWORD(_T("Software\\TortoiseGit\\VersionCheck"), TRUE
) != FALSE
&& !m_bForce
&& !m_bShowInfo
)
337 GetDlgItem(IDC_DONOTASKAGAIN
)->ShowWindow(SW_SHOW
);
338 rectOKButton
.left
+= 60;
340 temp
.LoadString(IDS_REMINDMELATER
);
341 GetDlgItem(IDOK
)->SetWindowText(temp
);
342 rectOKButton
.right
+= 160;
344 GetDlgItem(IDOK
)->MoveWindow(&rectOKButton
);
345 m_ctrlFiles
.ShowWindow(SW_SHOW
);
346 GetDlgItem(IDC_GROUP_DOWNLOADS
)->ShowWindow(SW_SHOW
);
350 else if (m_bShowInfo
)
352 temp
.LoadString(IDS_CHECKNEWER_YOURUPTODATE
);
353 SetDlgItemText(IDC_CHECKRESULT
, temp
);
354 FillChangelog(versioncheck
, official
);
359 if (!m_sUpdateDownloadLink
.IsEmpty())
361 m_link
.ShowWindow(SW_SHOW
);
362 m_link
.SetURL(m_sUpdateDownloadLink
);
364 m_bThreadRunning
= FALSE
;
365 DialogEnableWindow(IDOK
, TRUE
);
369 void CCheckForUpdatesDlg::FillDownloads(CAutoConfig
& versioncheck
, const CString version
)
372 const CString x86x64
= _T("64");
374 const CString x86x64
= _T("32");
377 versioncheck
.GetString(_T("tortoisegit.baseurl"), m_sFilesURL
);
378 if (m_sFilesURL
.IsEmpty())
379 m_sFilesURL
.Format(_T("http://updater.download.tortoisegit.org/tgit/%s/"), version
);
381 bool isHotfix
= false;
382 versioncheck
.GetBool(_T("tortoisegit.hotfix"), isHotfix
);
385 m_ctrlFiles
.InsertItem(0, _T("TortoiseGit Hotfix"));
387 m_ctrlFiles
.InsertItem(0, _T("TortoiseGit"));
388 CString filenameMain
, filenamePattern
;
389 versioncheck
.GetString(_T("tortoisegit.mainfilename"), filenamePattern
);
390 if (filenamePattern
.IsEmpty())
391 filenamePattern
= _T("TortoiseGit-%1!s!-%2!s!bit.msi");
392 filenameMain
.FormatMessage(filenamePattern
, version
, x86x64
);
393 m_ctrlFiles
.SetItemData(0, (DWORD_PTR
)(new CUpdateListCtrl::Entry(filenameMain
, CUpdateListCtrl::STATUS_NONE
)));
394 m_ctrlFiles
.SetCheck(0 , TRUE
);
398 DialogEnableWindow(IDC_BUTTON_UPDATE
, TRUE
);
412 std::vector
<LangPack
> availableLangs
;
413 std::vector
<DWORD
> installedLangs
;
416 // set up the language selecting combobox
417 CString path
= CPathUtils::GetAppParentDirectory();
418 path
= path
+ _T("Languages\\");
419 CSimpleFileFind
finder(path
, _T("*.dll"));
420 while (finder
.FindNextFileNoDirectories())
422 CString file
= finder
.GetFilePath();
423 CString filename
= finder
.GetFileName();
424 if (filename
.Left(12).CompareNoCase(_T("TortoiseProc")) == 0)
426 CString sVer
= _T(STRPRODUCTVER
);
427 sVer
= sVer
.Left(sVer
.ReverseFind('.'));
428 CString sFileVer
= CPathUtils::GetVersionFromFile(file
);
429 sFileVer
= sFileVer
.Left(sFileVer
.ReverseFind('.'));
430 CString sLoc
= filename
.Mid(12);
431 sLoc
= sLoc
.Left(sLoc
.GetLength() - 4); // cut off ".dll"
432 if ((sLoc
.Left(2) == L
"32") && (sLoc
.GetLength() > 5))
434 DWORD loc
= _tstoi(filename
.Mid(12));
435 languagePacks
.installedLangs
.push_back(loc
);
440 git_config_get_multivar_foreach(versioncheck
, "tortoisegit.langs", nullptr, [](const git_config_entry
* configentry
, void* payload
) -> int
442 LanguagePacks
* languagePacks
= (LanguagePacks
*)payload
;
443 CString langs
= CUnicodeUtils::GetUnicode(configentry
->value
);
445 int nextTokenPos
= langs
.Find(_T(";"), 5); // be extensible for the future
446 if (nextTokenPos
> 0)
447 langs
= langs
.Left(nextTokenPos
);
448 CString sLang
= _T("TortoiseGit Language Pack ") + langs
.Mid(5);
450 DWORD loc
= _tstoi(langs
.Mid(0, 4));
451 TCHAR buf
[MAX_PATH
] = { 0 };
452 GetLocaleInfo(loc
, LOCALE_SNATIVELANGNAME
, buf
, _countof(buf
));
454 GetLocaleInfo(loc
, LOCALE_SNATIVECTRYNAME
, buf
, _countof(buf
));
462 bool installed
= std::find(languagePacks
->installedLangs
.begin(), languagePacks
->installedLangs
.end(), loc
) != languagePacks
->installedLangs
.end();
463 LangPack pack
= { sLang
, sLang2
, loc
, langs
.Mid(5), installed
};
464 languagePacks
->availableLangs
.push_back(pack
);
468 std::stable_sort(languagePacks
.availableLangs
.begin(), languagePacks
.availableLangs
.end(), [&](const LangPack
& a
, const LangPack
& b
) -> int
470 return (a
.m_Installed
&& !b
.m_Installed
) ? 1 : (!a
.m_Installed
&& b
.m_Installed
) ? 0 : (a
.m_PackName
.Compare(b
.m_PackName
) < 0);
472 filenamePattern
.Empty();
473 versioncheck
.GetString(_T("tortoisegit.languagepackfilename"), filenamePattern
);
474 if (filenamePattern
.IsEmpty())
475 filenamePattern
= _T("TortoiseGit-LanguagePack-%1!s!-%2!s!bit-%3!s!.msi");
476 for (auto langs
: languagePacks
.availableLangs
)
478 int pos
= m_ctrlFiles
.InsertItem(m_ctrlFiles
.GetItemCount(), langs
.m_PackName
);
479 m_ctrlFiles
.SetItemText(pos
, 1, langs
.m_LangName
);
482 filename
.FormatMessage(filenamePattern
, version
, x86x64
, langs
.m_LangCode
, langs
.m_LocaleID
);
483 m_ctrlFiles
.SetItemData(pos
, (DWORD_PTR
)(new CUpdateListCtrl::Entry(filename
, CUpdateListCtrl::STATUS_NONE
)));
485 if (langs
.m_Installed
)
486 m_ctrlFiles
.SetCheck(pos
, TRUE
);
488 DialogEnableWindow(IDC_BUTTON_UPDATE
, TRUE
);
491 void CCheckForUpdatesDlg::FillChangelog(CAutoConfig
& versioncheck
, bool official
)
493 ProjectProperties pp
;
494 pp
.lProjectLanguage
= -1;
495 if (versioncheck
.GetString(_T("tortoisegit.issuesurl"), pp
.sUrl
))
496 pp
.sUrl
= _T("https://code.google.com/p/tortoisegit/issues/detail?id=%BUGID%");
497 if (!pp
.sUrl
.IsEmpty())
499 pp
.SetCheckRe(_T("[Ii]ssues?:?(\\s*(,|and)?\\s*#?\\d+)+"));
500 pp
.SetBugIDRe(_T("(\\d+)"));
502 m_cLogMessage
.Init(pp
);
504 CString sChangelogURL
;
505 versioncheck
.GetString(_T("TortoiseGit.changelogurl"), sChangelogURL
);
506 if (sChangelogURL
.IsEmpty())
507 sChangelogURL
= _T("https://versioncheck.tortoisegit.org/changelog.txt");
510 CString
tmp(sChangelogURL
);
511 sChangelogURL
.FormatMessage(tmp
, TGIT_VERMAJOR
, TGIT_VERMINOR
, TGIT_VERMICRO
, m_updateDownloader
->m_sWindowsPlatform
, m_updateDownloader
->m_sWindowsVersion
, m_updateDownloader
->m_sWindowsServicePack
);
514 CString tempchangelogfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
516 if ((err
= m_updateDownloader
->DownloadFile(sChangelogURL
, tempchangelogfile
, false)) != ERROR_SUCCESS
)
518 CString msg
= _T("Could not load changelog.\r\nError: ") + GetWinINetError(err
) + _T(" (on ") + sChangelogURL
+ _T(")");
519 ::SendMessage(m_hWnd
, WM_USER_FILLCHANGELOG
, 0, reinterpret_cast<LPARAM
>((LPCTSTR
)msg
));
524 CString signatureTempfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
525 if ((err
= m_updateDownloader
->DownloadFile(sChangelogURL
+ SIGNATURE_FILE_ENDING
, signatureTempfile
, false)) != ERROR_SUCCESS
|| VerifyIntegrity(tempchangelogfile
, signatureTempfile
, m_updateDownloader
))
527 CString error
= _T("Could not verify digital signature.");
529 error
+= _T("\r\nError: ") + GetWinINetError(err
) + _T(" (on ") + sChangelogURL
+ SIGNATURE_FILE_ENDING
+ _T(")");
530 ::SendMessage(m_hWnd
, WM_USER_FILLCHANGELOG
, 0, reinterpret_cast<LPARAM
>((LPCTSTR
)error
));
531 DeleteUrlCacheEntry(sChangelogURL
);
532 DeleteUrlCacheEntry(sChangelogURL
+ SIGNATURE_FILE_ENDING
);
539 if (file
.Open(tempchangelogfile
, CFile::modeRead
| CFile::typeBinary
))
541 std::unique_ptr
<BYTE
[]> buf(new BYTE
[(UINT
)file
.GetLength()]);
542 UINT read
= file
.Read(buf
.get(), (UINT
)file
.GetLength());
543 bool skipBom
= read
>= 3 && buf
[0] == 0xEF && buf
[1] == 0xBB && buf
[2] == 0xBF;
544 g_Git
.StringAppend(&temp
, buf
.get() + (skipBom
? 3 : 0), CP_UTF8
, read
- (skipBom
? 3 : 0));
547 temp
= _T("Could not open downloaded changelog file.");
548 ::SendMessage(m_hWnd
, WM_USER_FILLCHANGELOG
, 0, reinterpret_cast<LPARAM
>((LPCTSTR
)temp
));
551 void CCheckForUpdatesDlg::OnTimer(UINT_PTR nIDEvent
)
555 if (m_bThreadRunning
== FALSE
)
560 ShowWindow(SW_SHOWNORMAL
);
568 CStandAloneDialog::OnTimer(nIDEvent
);
571 void CCheckForUpdatesDlg::OnWindowPosChanging(WINDOWPOS
* lpwndpos
)
573 CStandAloneDialog::OnWindowPosChanging(lpwndpos
);
574 if (m_bVisible
== FALSE
)
575 lpwndpos
->flags
&= ~SWP_SHOWWINDOW
;
578 BOOL
CCheckForUpdatesDlg::OnSetCursor(CWnd
* pWnd
, UINT nHitTest
, UINT message
)
580 HCURSOR hCur
= LoadCursor(NULL
, MAKEINTRESOURCE(IDC_ARROW
));
582 return CStandAloneDialogTmpl
<CDialog
>::OnSetCursor(pWnd
, nHitTest
, message
);
585 void CCheckForUpdatesDlg::OnBnClickedButtonUpdate()
588 m_ctrlUpdate
.GetWindowText(title
);
589 if (m_pDownloadThread
== NULL
&& title
== CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD
)))
591 bool isOneSelected
= false;
592 for (int i
= 0; i
< (int)m_ctrlFiles
.GetItemCount(); ++i
)
594 if (m_ctrlFiles
.GetCheck(i
))
596 isOneSelected
= true;
603 m_eventStop
.ResetEvent();
605 m_pDownloadThread
= ::AfxBeginThread(DownloadThreadEntry
, this, THREAD_PRIORITY_NORMAL
, 0, CREATE_SUSPENDED
);
606 if (m_pDownloadThread
!= NULL
)
608 m_pDownloadThread
->m_bAutoDelete
= FALSE
;
609 m_pDownloadThread
->ResumeThread();
611 GetDlgItem(IDC_BUTTON_UPDATE
)->SetWindowText(CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)));
615 CMessageBox::Show(NULL
, IDS_ERR_THREADSTARTFAILED
, IDS_APPNAME
, MB_OK
| MB_ICONERROR
);
618 else if (title
== CString(MAKEINTRESOURCE(IDS_ABORTBUTTON
)))
621 m_eventStop
.SetEvent();
625 CString folder
= GetDownloadsDirectory();
626 if (m_ctrlUpdate
.GetCurrentEntry() == 0)
628 for (int i
= 0; i
< (int)m_ctrlFiles
.GetItemCount(); ++i
)
630 CUpdateListCtrl::Entry
*data
= (CUpdateListCtrl::Entry
*)m_ctrlFiles
.GetItemData(i
);
631 if (m_ctrlFiles
.GetCheck(i
) == TRUE
)
632 ShellExecute(NULL
, _T("open"), folder
+ data
->m_filename
, NULL
, NULL
, SW_SHOWNORMAL
);
634 CStandAloneDialog::OnOK();
636 else if (m_ctrlUpdate
.GetCurrentEntry() == 1)
638 ShellExecute(NULL
, _T("open"), folder
, NULL
, NULL
, SW_SHOWNORMAL
);
641 m_ctrlUpdate
.SetCurrentEntry(0);
645 UINT
CCheckForUpdatesDlg::DownloadThreadEntry(LPVOID pVoid
)
647 return ((CCheckForUpdatesDlg
*)pVoid
)->DownloadThread();
650 bool CCheckForUpdatesDlg::Download(CString filename
)
652 CString url
= m_sFilesURL
+ filename
;
653 CString destFilename
= GetDownloadsDirectory() + filename
;
654 if (PathFileExists(destFilename
) && PathFileExists(destFilename
+ SIGNATURE_FILE_ENDING
))
656 if (VerifyIntegrity(destFilename
, destFilename
+ SIGNATURE_FILE_ENDING
, m_updateDownloader
) == 0)
660 DeleteFile(destFilename
);
661 DeleteFile(destFilename
+ SIGNATURE_FILE_ENDING
);
662 DeleteUrlCacheEntry(url
);
663 DeleteUrlCacheEntry(url
+ SIGNATURE_FILE_ENDING
);
667 m_progress
.SetRange32(0, 1);
668 m_progress
.SetPos(0);
669 m_progress
.ShowWindow(SW_SHOW
);
672 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
673 m_pTaskbarList
->SetProgressValue(m_hWnd
, 0, 1);
676 CString tempfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
677 CString signatureTempfile
= CTempFiles::Instance().GetTempFilePath(true).GetWinPathString();
678 DWORD ret
= m_updateDownloader
->DownloadFile(url
, tempfile
, true);
681 ret
= m_updateDownloader
->DownloadFile(url
+ SIGNATURE_FILE_ENDING
, signatureTempfile
, true);
683 m_sErrors
+= url
+ SIGNATURE_FILE_ENDING
+ _T(": ") + GetWinINetError(ret
) + _T("\r\n");
684 m_progress
.SetPos(m_progress
.GetPos() + 1);
687 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
688 int minValue
, maxValue
;
689 m_progress
.GetRange(minValue
, maxValue
);
690 m_pTaskbarList
->SetProgressValue(m_hWnd
, m_progress
.GetPos(), maxValue
);
694 m_sErrors
+= url
+ _T(": ") + GetWinINetError(ret
) + _T("\r\n");
697 if (VerifyIntegrity(tempfile
, signatureTempfile
, m_updateDownloader
) == 0)
699 DeleteFile(destFilename
);
700 DeleteFile(destFilename
+ SIGNATURE_FILE_ENDING
);
701 MoveFile(tempfile
, destFilename
);
702 MoveFile(signatureTempfile
, destFilename
+ SIGNATURE_FILE_ENDING
);
705 m_sErrors
+= url
+ SIGNATURE_FILE_ENDING
+ _T(": Broken digital signature.\r\n");
706 DeleteUrlCacheEntry(url
);
707 DeleteUrlCacheEntry(url
+ SIGNATURE_FILE_ENDING
);
712 UINT
CCheckForUpdatesDlg::DownloadThread()
714 m_ctrlFiles
.SetExtendedStyle(m_ctrlFiles
.GetExtendedStyle() & ~LVS_EX_CHECKBOXES
);
717 for (int i
= 0; i
< (int)m_ctrlFiles
.GetItemCount(); ++i
)
719 m_ctrlFiles
.EnsureVisible(i
, FALSE
);
721 m_ctrlFiles
.GetItemRect(i
, &rect
, LVIR_BOUNDS
);
722 CUpdateListCtrl::Entry
*data
= (CUpdateListCtrl::Entry
*)m_ctrlFiles
.GetItemData(i
);
723 if (m_ctrlFiles
.GetCheck(i
) == TRUE
)
725 data
->m_status
= CUpdateListCtrl::STATUS_DOWNLOADING
;
726 m_ctrlFiles
.InvalidateRect(rect
);
727 if (Download(data
->m_filename
))
728 data
->m_status
= CUpdateListCtrl::STATUS_SUCCESS
;
731 data
->m_status
= CUpdateListCtrl::STATUS_FAIL
;
736 data
->m_status
= CUpdateListCtrl::STATUS_IGNORE
;
737 m_ctrlFiles
.InvalidateRect(rect
);
740 ::PostMessage(GetSafeHwnd(), WM_USER_ENDDOWNLOAD
, 0, 0);
745 LRESULT
CCheckForUpdatesDlg::OnEndDownload(WPARAM
, LPARAM
)
747 ASSERT(m_pDownloadThread
!= NULL
);
749 // wait until the thread terminates
751 if (::GetExitCodeThread(m_pDownloadThread
->m_hThread
, &dwExitCode
) && dwExitCode
== STILL_ACTIVE
)
752 ::WaitForSingleObject(m_pDownloadThread
->m_hThread
, INFINITE
);
754 // make sure we always have the correct exit code
755 ::GetExitCodeThread(m_pDownloadThread
->m_hThread
, &dwExitCode
);
757 delete m_pDownloadThread
;
758 m_pDownloadThread
= NULL
;
760 m_progress
.ShowWindow(SW_HIDE
);
762 if (dwExitCode
== TRUE
)
764 m_ctrlUpdate
.AddEntry(CString(MAKEINTRESOURCE(IDS_PROC_INSTALL
)));
765 m_ctrlUpdate
.AddEntry(CString(MAKEINTRESOURCE(IDS_CHECKUPDATE_DESTFOLDER
)));
766 m_ctrlUpdate
.Invalidate();
768 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
);
772 m_ctrlUpdate
.SetWindowText(CString(MAKEINTRESOURCE(IDS_PROC_DOWNLOAD
)));
774 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
776 tmp
.LoadString(IDS_ERR_FAILEDUPDATEDOWNLOAD
);
777 if (!m_sErrors
.IsEmpty())
778 tmp
+= _T("\r\n\r\nErrors:\r\n") + m_sErrors
;
779 CMessageBox::Show(NULL
, tmp
, _T("TortoiseGit"), MB_ICONERROR
);
781 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
);
787 LRESULT
CCheckForUpdatesDlg::OnFillChangelog(WPARAM
, LPARAM lParam
)
789 ASSERT(lParam
!= NULL
);
791 LPCTSTR changelog
= reinterpret_cast<LPCTSTR
>(lParam
);
792 m_cLogMessage
.Call(SCI_SETREADONLY
, FALSE
);
793 m_cLogMessage
.SetText(changelog
);
794 m_cLogMessage
.Call(SCI_SETREADONLY
, TRUE
);
795 m_cLogMessage
.Call(SCI_GOTOPOS
, 0);
800 CString
CCheckForUpdatesDlg::GetDownloadsDirectory()
804 if (SysInfo::Instance().IsVistaOrLater())
806 CAutoLibrary hShell
= AtlLoadSystemLibraryUsingFullPath(_T("shell32.dll"));
809 typedef HRESULT STDAPICALLTYPE
SHGetKnownFolderPathFN(__in REFKNOWNFOLDERID rfid
, __in DWORD dwFlags
, __in_opt HANDLE hToken
, __deref_out PWSTR
*ppszPath
);
810 SHGetKnownFolderPathFN
*pfnSHGetKnownFolderPath
= (SHGetKnownFolderPathFN
*)GetProcAddress(hShell
, "SHGetKnownFolderPath");
811 if (pfnSHGetKnownFolderPath
)
813 wchar_t * wcharPtr
= 0;
814 HRESULT hr
= pfnSHGetKnownFolderPath(FOLDERID_Downloads
, KF_FLAG_CREATE
, NULL
, &wcharPtr
);
818 CoTaskMemFree(static_cast<void*>(wcharPtr
));
819 return folder
.TrimRight(_T("\\")) + _T("\\");
825 TCHAR szPath
[MAX_PATH
] = {0};
826 if (SUCCEEDED(SHGetFolderPath(NULL
, CSIDL_PERSONAL
| CSIDL_FLAG_CREATE
, NULL
, SHGFP_TYPE_CURRENT
, szPath
)))
828 CString downloads
= folder
.TrimRight(_T("\\")) + _T("\\Downloads\\");
829 if ((PathFileExists(downloads
) && PathIsDirectory(downloads
)) || (!PathFileExists(downloads
) && CreateDirectory(downloads
, NULL
)))
835 LRESULT
CCheckForUpdatesDlg::OnDisplayStatus(WPARAM
, LPARAM lParam
)
837 const CUpdateDownloader::DOWNLOADSTATUS
*const pDownloadStatus
= reinterpret_cast<CUpdateDownloader::DOWNLOADSTATUS
*>(lParam
);
838 if (pDownloadStatus
!= NULL
)
840 ASSERT(::AfxIsValidAddress(pDownloadStatus
, sizeof(CUpdateDownloader::DOWNLOADSTATUS
)));
842 m_progress
.SetRange32(0, pDownloadStatus
->ulProgressMax
);
843 m_progress
.SetPos(pDownloadStatus
->ulProgress
);
846 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
847 m_pTaskbarList
->SetProgressValue(m_hWnd
, pDownloadStatus
->ulProgress
, pDownloadStatus
->ulProgressMax
);
854 LRESULT
CCheckForUpdatesDlg::OnTaskbarBtnCreated(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
856 m_pTaskbarList
.Release();
857 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);
861 CString
CCheckForUpdatesDlg::GetWinINetError(DWORD err
)
863 CString readableError
= CFormatMessageWrapper(err
);
864 if (readableError
.IsEmpty())
866 CString modules
[] = { _T("wininet.dll"), _T("urlmon.dll") };
867 for (auto module
: modules
)
870 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_FROM_HMODULE
, GetModuleHandle(module
), err
, 0, (LPTSTR
)&buffer
, 0, NULL
);
871 readableError
= buffer
;
873 if (!readableError
.IsEmpty())
877 return readableError
.Trim();
880 void CCheckForUpdatesDlg::OnBnClickedDonotaskagain()
882 if (CMessageBox::Show(GetSafeHwnd(), IDS_DISABLEUPDATECHECKS
, IDS_APPNAME
, 2, IDI_QUESTION
, IDS_DISABLEUPDATECHECKSBUTTON
, IDS_ABORTBUTTON
) == 1)
884 CRegDWORD(_T("Software\\TortoiseGit\\VersionCheck")) = FALSE
;