1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2017, 2019-2023 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "UpdateDownloader.h"
21 #include "../version.h"
23 CUpdateDownloader::CUpdateDownloader(HWND hwnd
, const CString
& sVersion
, bool force
, UINT msg
, CEvent
*eventStop
)
27 , m_eventStop(eventStop
)
29 OSVERSIONINFOEX inf
= {0};
30 if (!GetTrueWindowsVersion(inf
))
31 BruteforceGetWindowsVersionNumber(inf
);
33 m_sWindowsPlatform
= (inf
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) ? L
"NT" : L
"";
34 if (inf
.dwBuildNumber
&& inf
.dwMajorVersion
>= 10)
35 m_sWindowsVersion
.Format(L
"%ld.%ld.%ld", inf
.dwMajorVersion
, inf
.dwMinorVersion
, inf
.dwBuildNumber
);
37 m_sWindowsVersion
.Format(L
"%ld.%ld", inf
.dwMajorVersion
, inf
.dwMinorVersion
);
38 if (inf
.wServicePackMajor
)
39 m_sWindowsServicePack
.Format(L
"SP%ld", inf
.wServicePackMajor
);
42 userAgent
.Format(L
"TortoiseGit %s; %s; Windows%s%s %s%s%s", static_cast<LPCWSTR
>(sVersion
), _T(TGIT_PLATFORM
), m_sWindowsPlatform
.IsEmpty() ? L
"" : L
" ", static_cast<LPCWSTR
>(m_sWindowsPlatform
), static_cast<LPCWSTR
>(m_sWindowsVersion
), m_sWindowsServicePack
.IsEmpty() ? L
"" : L
" ", static_cast<LPCWSTR
>(m_sWindowsServicePack
));
43 hOpenHandle
= InternetOpen(userAgent
, INTERNET_OPEN_TYPE_PRECONFIG
, nullptr, nullptr, 0);
46 CUpdateDownloader::~CUpdateDownloader()
49 InternetCloseHandle(hOpenHandle
);
52 bool CUpdateDownloader::GetTrueWindowsVersion(OSVERSIONINFOEX
& pOSversion
)
54 using RtlGetVersion_FUNC
= LONG(WINAPI
*)(OSVERSIONINFOEXW
*);
56 CAutoLibrary hNTdllDll
= ::LoadLibrary(L
"ntdll.dll");
60 RtlGetVersion_FUNC pRtlGetVersion
= (RtlGetVersion_FUNC
)::GetProcAddress(hNTdllDll
, "RtlGetVersion");
64 return pRtlGetVersion(&pOSversion
) == 0;
67 void CUpdateDownloader::BruteforceGetWindowsVersionNumber(OSVERSIONINFOEX
& osVersionInfo
)
69 osVersionInfo
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEX
);
70 osVersionInfo
.dwMajorVersion
= HIBYTE(_WIN32_WINNT_WIN7
);
71 osVersionInfo
.dwMinorVersion
= LOBYTE(_WIN32_WINNT_WIN7
);
72 osVersionInfo
.dwPlatformId
= VER_PLATFORM_WIN32_NT
;
74 ULONGLONG maskConditioMajor
= ::VerSetConditionMask(0, VER_MAJORVERSION
, VER_LESS
);
75 ULONGLONG maskConditioMinor
= ::VerSetConditionMask(0, VER_MINORVERSION
, VER_LESS
);
76 ULONGLONG maskConditioSPMajor
= ::VerSetConditionMask(0, VER_SERVICEPACKMAJOR
, VER_LESS
);
77 while (!::VerifyVersionInfo(&osVersionInfo
, VER_MAJORVERSION
, maskConditioMajor
))
79 ++osVersionInfo
.dwMajorVersion
;
80 osVersionInfo
.dwMinorVersion
= 0;
81 osVersionInfo
.wServicePackMajor
= 0;
82 osVersionInfo
.wServicePackMinor
= 0;
84 while (!::VerifyVersionInfo(&osVersionInfo
, VER_MINORVERSION
, maskConditioMinor
))
86 ++osVersionInfo
.dwMinorVersion
;
87 osVersionInfo
.wServicePackMajor
= 0;
88 osVersionInfo
.wServicePackMinor
= 0;
90 while (!::VerifyVersionInfo(&osVersionInfo
, VER_SERVICEPACKMAJOR
, maskConditioSPMajor
))
91 ++osVersionInfo
.wServicePackMajor
;
92 // detection of VER_SERVICEPACKMINOR doesn't work reliably
95 DWORD
CUpdateDownloader::DownloadFile(const CString
& url
, const CString
& dest
, bool showProgress
) const
99 URL_COMPONENTS urlComponents
= {0};
100 urlComponents
.dwStructSize
= sizeof(urlComponents
);
101 urlComponents
.lpszHostName
= hostname
.GetBufferSetLength(INTERNET_MAX_HOST_NAME_LENGTH
);
102 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
103 urlComponents
.lpszUrlPath
= urlpath
.GetBufferSetLength(INTERNET_MAX_PATH_LENGTH
);
104 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
105 if (!InternetCrackUrl(url
, url
.GetLength(), 0, &urlComponents
))
106 return GetLastError();
107 hostname
.ReleaseBuffer();
108 urlpath
.ReleaseBuffer();
111 DeleteUrlCacheEntry(url
);
114 BOOL enableDecoding
= InternetSetOption(hOpenHandle
, INTERNET_OPTION_HTTP_DECODING
, &bTrue
, sizeof(bTrue
));
116 bool isHttps
= urlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
;
117 HINTERNET hConnectHandle
= InternetConnect(hOpenHandle
, hostname
, urlComponents
.nPort
, nullptr, nullptr, isHttps
? INTERNET_SCHEME_HTTP
: urlComponents
.nScheme
, 0, 0);
120 DWORD err
= GetLastError();
121 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s failed on InternetConnect: %d\n", static_cast<LPCWSTR
>(url
), err
);
124 SCOPE_EXIT
{ InternetCloseHandle(hConnectHandle
); };
125 HINTERNET hResourceHandle
= HttpOpenRequest(hConnectHandle
, nullptr, urlpath
, nullptr, nullptr, nullptr, INTERNET_FLAG_KEEP_CONNECTION
| (isHttps
? INTERNET_FLAG_SECURE
: 0) | (m_bForce
? INTERNET_FLAG_HYPERLINK
: 0), 0);
126 if (!hResourceHandle
)
128 DWORD err
= GetLastError();
129 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s failed on HttpOpenRequest: %d\n", static_cast<LPCWSTR
>(url
), err
);
132 SCOPE_EXIT
{ InternetCloseHandle(hResourceHandle
); };
135 HttpAddRequestHeaders(hResourceHandle
, L
"Accept-Encoding: gzip, deflate\r\n", DWORD(-1), HTTP_ADDREQ_FLAG_ADD
);
139 BOOL httpsendrequest
= HttpSendRequest(hResourceHandle
, nullptr, 0, nullptr, 0);
141 DWORD dwError
= InternetErrorDlg(m_hWnd
, hResourceHandle
, ERROR_SUCCESS
, FLAGS_ERROR_UI_FILTER_FOR_ERRORS
| FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS
| FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
, nullptr);
143 if (dwError
== ERROR_INTERNET_FORCE_RETRY
)
145 else if (!httpsendrequest
)
147 DWORD err
= GetLastError();
148 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s failed: %d, %d\n", static_cast<LPCWSTR
>(url
), httpsendrequest
, err
);
153 DWORD contentLength
= 0;
155 DWORD length
= sizeof(contentLength
);
156 HttpQueryInfo(hResourceHandle
, HTTP_QUERY_CONTENT_LENGTH
| HTTP_QUERY_FLAG_NUMBER
, &contentLength
, &length
, nullptr);
159 DWORD statusCode
= 0;
160 DWORD length
= sizeof(statusCode
);
161 if (!HttpQueryInfo(hResourceHandle
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
, &statusCode
, &length
, nullptr) || statusCode
!= 200)
163 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s returned %d\n", static_cast<LPCWSTR
>(url
), statusCode
);
164 if (statusCode
== 404)
165 return ERROR_FILE_NOT_FOUND
;
166 else if (statusCode
== 403)
167 return ERROR_ACCESS_DENIED
;
168 return static_cast<DWORD
>(INET_E_DOWNLOAD_FAILURE
);
172 CFile destinationFile
;
173 if (!destinationFile
.Open(dest
, CFile::modeCreate
| CFile::modeWrite
))
175 return ERROR_OPEN_FAILED
;
177 DWORD downloadedSum
= 0; // sum of bytes downloaded so far
178 constexpr DWORD BUFFER_SIZE
= 65536;
179 auto buff
= std::make_unique
<char[]>(BUFFER_SIZE
);
182 DWORD size
; // size of the data available
183 if (!InternetQueryDataAvailable(hResourceHandle
, &size
, 0, 0))
185 DWORD err
= GetLastError();
186 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s failed on InternetQueryDataAvailable: %d\n", static_cast<LPCWSTR
>(url
), err
);
190 DWORD downloaded
; // size of the downloaded data
191 if (!InternetReadFile(hResourceHandle
, buff
.get(), min(BUFFER_SIZE
, size
), &downloaded
))
193 DWORD err
= GetLastError();
194 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download of %s failed on InternetReadFile: %d\n", static_cast<LPCWSTR
>(url
), err
);
203 destinationFile
.Write(buff
.get(), downloaded
);
205 catch (CFileException
* e
)
207 destinationFile
.Close();
209 auto ret
= e
->m_lOsError
;
210 if (e
->m_cause
== CFileException::diskFull
)
211 ret
= ERROR_DISK_FULL
;
216 if (DWordAdd(downloadedSum
, downloaded
, &downloadedSum
) != S_OK
)
217 downloadedSum
= DWORD_MAX
- 1;
222 ASSERT(m_uiMsg
&& m_eventStop
);
224 if (contentLength
== 0) // got no content-length from webserver
226 DOWNLOADSTATUS downloadStatus
= { 0, 1 + 1 }; // + 1 for download of signature file
227 ::SendMessage(m_hWnd
, m_uiMsg
, 0, reinterpret_cast<LPARAM
>(&downloadStatus
));
231 if (downloadedSum
> contentLength
)
232 downloadedSum
= contentLength
- 1;
233 DOWNLOADSTATUS downloadStatus
= { downloadedSum
, contentLength
+ 1 }; // + 1 for download of signature file
234 ::SendMessage(m_hWnd
, m_uiMsg
, 0, reinterpret_cast<LPARAM
>(&downloadStatus
));
237 if (::WaitForSingleObject(*m_eventStop
, 0) == WAIT_OBJECT_0
)
239 return static_cast<DWORD
>(E_ABORT
); // canceled by the user
244 if (downloadedSum
== 0)
246 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) L
": Download size of %s was zero.\n", static_cast<LPCWSTR
>(url
));
247 return static_cast<DWORD
>(INET_E_DOWNLOAD_FAILURE
);
249 return ERROR_SUCCESS
;