Merge branch 'git-describe'
[TortoiseGit.git] / src / TortoiseProc / UpdateDownloader.cpp
bloba3a318b00ce83c67f26bba187e083805e2d3a985
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013-2014 - 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, 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 BruteforceGetWindowsVersionNumber(inf);
32 CString userAgent;
33 userAgent.Format(L"TortoiseGit %s; %s; Windows%s %ld.%ld", _T(STRFILEVER), _T(TGIT_PLATFORM), (inf.dwPlatformId == VER_PLATFORM_WIN32_NT) ? _T(" NT") : _T(""), inf.dwMajorVersion, inf.dwMinorVersion);
34 hOpenHandle = InternetOpen(userAgent, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0);
37 CUpdateDownloader::~CUpdateDownloader(void)
39 if (hOpenHandle)
40 InternetCloseHandle(hOpenHandle);
43 void CUpdateDownloader::BruteforceGetWindowsVersionNumber(OSVERSIONINFOEX& osVersionInfo)
45 osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
46 GetVersionEx((OSVERSIONINFO *)&osVersionInfo);
48 ULONGLONG maskConditioMajor = ::VerSetConditionMask(0, VER_MAJORVERSION, VER_LESS);
49 ULONGLONG maskConditioMinor = ::VerSetConditionMask(0, VER_MINORVERSION, VER_LESS);
50 while (!::VerifyVersionInfo(&osVersionInfo, VER_MAJORVERSION, maskConditioMajor))
52 ++osVersionInfo.dwMajorVersion;
53 osVersionInfo.dwMinorVersion = 0;
55 while (!::VerifyVersionInfo(&osVersionInfo, VER_MINORVERSION, maskConditioMinor))
56 ++osVersionInfo.dwMinorVersion;
59 BOOL CUpdateDownloader::DownloadFile(const CString& url, const CString& dest, bool showProgress) const
61 CString hostname;
62 CString urlpath;
63 URL_COMPONENTS urlComponents = {0};
64 urlComponents.dwStructSize = sizeof(urlComponents);
65 urlComponents.lpszHostName = hostname.GetBufferSetLength(INTERNET_MAX_HOST_NAME_LENGTH);
66 urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
67 urlComponents.lpszUrlPath = urlpath.GetBufferSetLength(INTERNET_MAX_PATH_LENGTH);
68 urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
69 if (!InternetCrackUrl(url, url.GetLength(), 0, &urlComponents))
70 return GetLastError();
71 hostname.ReleaseBuffer();
72 urlpath.ReleaseBuffer();
74 if (m_bForce)
75 DeleteUrlCacheEntry(url);
77 bool isHttps = urlComponents.nScheme == INTERNET_SCHEME_HTTPS;
78 HINTERNET hConnectHandle = InternetConnect(hOpenHandle, hostname, urlComponents.nPort, nullptr, nullptr, isHttps ? INTERNET_SCHEME_HTTP : urlComponents.nScheme, 0, 0);
79 if (!hConnectHandle)
81 DWORD err = GetLastError();
82 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s failed on InternetConnect: %d\n"), url, err);
83 return err;
85 HINTERNET hResourceHandle = HttpOpenRequest(hConnectHandle, nullptr, urlpath, nullptr, nullptr, nullptr, INTERNET_FLAG_KEEP_CONNECTION | (isHttps ? INTERNET_FLAG_SECURE : 0), 0);
86 if (!hResourceHandle)
88 DWORD err = GetLastError();
89 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s failed on HttpOpenRequest: %d\n"), url, err);
90 InternetCloseHandle(hConnectHandle);
91 return err;
95 resend:
96 BOOL httpsendrequest = HttpSendRequest(hResourceHandle, nullptr, 0, nullptr, 0);
98 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);
100 if (dwError == ERROR_INTERNET_FORCE_RETRY)
101 goto resend;
102 else if (!httpsendrequest)
104 DWORD err = GetLastError();
105 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s failed: %d, %d\n"), url, httpsendrequest, err);
106 InternetCloseHandle(hResourceHandle);
107 InternetCloseHandle(hConnectHandle);
108 return err;
112 DWORD contentLength = 0;
114 DWORD length = sizeof(contentLength);
115 HttpQueryInfo(hResourceHandle, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&contentLength, &length, NULL);
118 DWORD statusCode = 0;
119 DWORD length = sizeof(statusCode);
120 if (!HttpQueryInfo(hResourceHandle, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&statusCode, &length, NULL) || statusCode != 200)
122 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s returned %d\n"), url, statusCode);
123 InternetCloseHandle(hResourceHandle);
124 InternetCloseHandle(hConnectHandle);
125 if (statusCode == 404)
126 return ERROR_FILE_NOT_FOUND;
127 else if (statusCode == 403)
128 return ERROR_ACCESS_DENIED;
129 return INET_E_DOWNLOAD_FAILURE;
133 CFile destinationFile;
134 if (!destinationFile.Open(dest, CFile::modeCreate | CFile::modeWrite))
136 InternetCloseHandle(hResourceHandle);
137 InternetCloseHandle(hConnectHandle);
138 return ERROR_ACCESS_DENIED;
140 DWORD downloadedSum = 0; // sum of bytes downloaded so far
143 DWORD size; // size of the data available
144 if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))
146 DWORD err = GetLastError();
147 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s failed on InternetQueryDataAvailable: %d\n"), url, err);
148 InternetCloseHandle(hResourceHandle);
149 InternetCloseHandle(hConnectHandle);
150 return err;
153 DWORD downloaded; // size of the downloaded data
154 LPTSTR lpszData = new TCHAR[size + 1];
155 if (!InternetReadFile(hResourceHandle, (LPVOID)lpszData, size, &downloaded))
157 delete[] lpszData;
158 DWORD err = GetLastError();
159 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download of %s failed on InternetReadFile: %d\n"), url, err);
160 InternetCloseHandle(hResourceHandle);
161 InternetCloseHandle(hConnectHandle);
162 return err;
165 if (downloaded == 0)
167 delete[] lpszData;
168 break;
171 lpszData[downloaded] = '\0';
172 destinationFile.Write(lpszData, downloaded);
173 delete[] lpszData;
175 downloadedSum += downloaded;
177 if (!showProgress)
178 continue;
180 ASSERT(m_uiMsg && m_eventStop);
182 if (contentLength == 0) // got no content-length from webserver
184 DOWNLOADSTATUS downloadStatus = { 0, 1 + 1 }; // + 1 for download of signature file
185 ::SendMessage(m_hWnd, m_uiMsg, 0, reinterpret_cast<LPARAM>(&downloadStatus));
187 else
189 if (downloadedSum > contentLength)
190 downloadedSum = contentLength - 1;
191 DOWNLOADSTATUS downloadStatus = { downloadedSum, contentLength + 1 }; // + 1 for download of signature file
192 ::SendMessage(m_hWnd, m_uiMsg, 0, reinterpret_cast<LPARAM>(&downloadStatus));
195 if (::WaitForSingleObject(*m_eventStop, 0) == WAIT_OBJECT_0)
197 InternetCloseHandle(hResourceHandle);
198 InternetCloseHandle(hConnectHandle);
199 return E_ABORT; // canceled by the user
202 while (true);
203 destinationFile.Close();
204 InternetCloseHandle(hResourceHandle);
205 InternetCloseHandle(hConnectHandle);
206 if (downloadedSum == 0)
208 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Download size of %s was zero.\n"), url);
209 return INET_E_DOWNLOAD_FAILURE;
211 return 0;