Fix piping to TortoiseGitUDiff by checking for ERROR_BROKEN_PIPE and treating that...
[TortoiseGit.git] / src / TortoiseProc / UpdateDownloader.cpp
blob4864a2749e252989015028a9335a928a6457504e
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.
19 #include "stdafx.h"
20 #include "UpdateDownloader.h"
21 #include "../version.h"
23 CUpdateDownloader::CUpdateDownloader(HWND hwnd, const CString& sVersion, bool force, UINT msg, CEvent *eventStop)
24 : m_hWnd(hwnd)
25 , m_bForce(force)
26 , m_uiMsg(msg)
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);
36 else
37 m_sWindowsVersion.Format(L"%ld.%ld", inf.dwMajorVersion, inf.dwMinorVersion);
38 if (inf.wServicePackMajor)
39 m_sWindowsServicePack.Format(L"SP%ld", inf.wServicePackMajor);
41 CString userAgent;
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()
48 if (hOpenHandle)
49 InternetCloseHandle(hOpenHandle);
52 bool CUpdateDownloader::GetTrueWindowsVersion(OSVERSIONINFOEX& pOSversion)
54 using RtlGetVersion_FUNC = LONG(WINAPI*)(OSVERSIONINFOEXW*);
56 CAutoLibrary hNTdllDll = ::LoadLibrary(L"ntdll.dll");
57 if (!hNTdllDll)
58 return false;
60 RtlGetVersion_FUNC pRtlGetVersion = (RtlGetVersion_FUNC)::GetProcAddress(hNTdllDll, "RtlGetVersion");
61 if (!pRtlGetVersion)
62 return false;
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
97 CString hostname;
98 CString urlpath;
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();
110 if (m_bForce)
111 DeleteUrlCacheEntry(url);
113 BOOL bTrue = TRUE;
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);
118 if (!hConnectHandle)
120 DWORD err = GetLastError();
121 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": Download of %s failed on InternetConnect: %d\n", static_cast<LPCWSTR>(url), err);
122 return 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);
130 return err;
132 SCOPE_EXIT{ InternetCloseHandle(hResourceHandle); };
134 if (enableDecoding)
135 HttpAddRequestHeaders(hResourceHandle, L"Accept-Encoding: gzip, deflate\r\n", DWORD(-1), HTTP_ADDREQ_FLAG_ADD);
138 resend:
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)
144 goto resend;
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);
149 return 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);
187 return 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);
195 return err;
198 if (downloaded == 0)
199 break;
203 destinationFile.Write(buff.get(), downloaded);
205 catch (CFileException* e)
207 destinationFile.Close();
208 ::DeleteFile(dest);
209 auto ret = e->m_lOsError;
210 if (e->m_cause == CFileException::diskFull)
211 ret = ERROR_DISK_FULL;
212 e->Delete();
213 return ret;
216 if (DWordAdd(downloadedSum, downloaded, &downloadedSum) != S_OK)
217 downloadedSum = DWORD_MAX - 1;
219 if (!showProgress)
220 continue;
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));
229 else
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
242 while (true);
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;