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.
20 #include "UpdateDownloader.h"
21 #include "..\version.h"
24 CUpdateDownloader::CUpdateDownloader(HWND hwnd
, bool force
, UINT msg
, CEvent
*eventStop
)
28 , m_eventStop(eventStop
)
30 OSVERSIONINFOEX inf
= {0};
31 BruteforceGetWindowsVersionNumber(inf
);
33 m_sWindowsPlatform
= (inf
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) ? _T("NT") : _T("");
34 m_sWindowsVersion
.Format(L
"%ld.%ld", inf
.dwMajorVersion
, inf
.dwMinorVersion
);
35 if (inf
.wServicePackMajor
)
36 m_sWindowsServicePack
.Format(L
"SP%ld", inf
.wServicePackMajor
);
39 userAgent
.Format(L
"TortoiseGit %s; %s; Windows%s%s %s%s%s", _T(STRFILEVER
), _T(TGIT_PLATFORM
), m_sWindowsPlatform
.IsEmpty() ? _T("") : _T(" "), m_sWindowsPlatform
, m_sWindowsVersion
, m_sWindowsServicePack
.IsEmpty() ? _T("") : _T(" "), m_sWindowsServicePack
);
40 hOpenHandle
= InternetOpen(userAgent
, INTERNET_OPEN_TYPE_PRECONFIG
, nullptr, nullptr, 0);
43 CUpdateDownloader::~CUpdateDownloader(void)
46 InternetCloseHandle(hOpenHandle
);
49 void CUpdateDownloader::BruteforceGetWindowsVersionNumber(OSVERSIONINFOEX
& osVersionInfo
)
51 osVersionInfo
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEX
);
52 GetVersionEx((OSVERSIONINFO
*)&osVersionInfo
);
54 ULONGLONG maskConditioMajor
= ::VerSetConditionMask(0, VER_MAJORVERSION
, VER_LESS
);
55 ULONGLONG maskConditioMinor
= ::VerSetConditionMask(0, VER_MINORVERSION
, VER_LESS
);
56 ULONGLONG maskConditioSPMajor
= ::VerSetConditionMask(0, VER_SERVICEPACKMAJOR
, VER_LESS
);
57 while (!::VerifyVersionInfo(&osVersionInfo
, VER_MAJORVERSION
, maskConditioMajor
))
59 ++osVersionInfo
.dwMajorVersion
;
60 osVersionInfo
.dwMinorVersion
= 0;
61 osVersionInfo
.wServicePackMajor
= 0;
62 osVersionInfo
.wServicePackMinor
= 0;
64 while (!::VerifyVersionInfo(&osVersionInfo
, VER_MINORVERSION
, maskConditioMinor
))
66 ++osVersionInfo
.dwMinorVersion
;
67 osVersionInfo
.wServicePackMajor
= 0;
68 osVersionInfo
.wServicePackMinor
= 0;
70 while (!::VerifyVersionInfo(&osVersionInfo
, VER_SERVICEPACKMAJOR
, maskConditioSPMajor
))
71 ++osVersionInfo
.wServicePackMajor
;
72 // detection of VER_SERVICEPACKMINOR doesn't work reliably
75 DWORD
CUpdateDownloader::DownloadFile(const CString
& url
, const CString
& dest
, bool showProgress
) const
79 URL_COMPONENTS urlComponents
= {0};
80 urlComponents
.dwStructSize
= sizeof(urlComponents
);
81 urlComponents
.lpszHostName
= hostname
.GetBufferSetLength(INTERNET_MAX_HOST_NAME_LENGTH
);
82 urlComponents
.dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
83 urlComponents
.lpszUrlPath
= urlpath
.GetBufferSetLength(INTERNET_MAX_PATH_LENGTH
);
84 urlComponents
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
85 if (!InternetCrackUrl(url
, url
.GetLength(), 0, &urlComponents
))
86 return GetLastError();
87 hostname
.ReleaseBuffer();
88 urlpath
.ReleaseBuffer();
91 DeleteUrlCacheEntry(url
);
94 BOOL enableDecoding
= InternetSetOption(hOpenHandle
, INTERNET_OPTION_HTTP_DECODING
, &bTrue
, sizeof(bTrue
));
96 bool isHttps
= urlComponents
.nScheme
== INTERNET_SCHEME_HTTPS
;
97 HINTERNET hConnectHandle
= InternetConnect(hOpenHandle
, hostname
, urlComponents
.nPort
, nullptr, nullptr, isHttps
? INTERNET_SCHEME_HTTP
: urlComponents
.nScheme
, 0, 0);
100 DWORD err
= GetLastError();
101 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s failed on InternetConnect: %d\n"), url
, err
);
104 HINTERNET hResourceHandle
= HttpOpenRequest(hConnectHandle
, nullptr, urlpath
, nullptr, nullptr, nullptr, INTERNET_FLAG_KEEP_CONNECTION
| (isHttps
? INTERNET_FLAG_SECURE
: 0), 0);
105 if (!hResourceHandle
)
107 DWORD err
= GetLastError();
108 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s failed on HttpOpenRequest: %d\n"), url
, err
);
109 InternetCloseHandle(hConnectHandle
);
113 if (enableDecoding
&& SysInfo::Instance().IsVistaOrLater())
114 HttpAddRequestHeaders(hResourceHandle
, L
"Accept-Encoding: gzip, deflate\r\n", (DWORD
)-1, HTTP_ADDREQ_FLAG_ADD
);
118 BOOL httpsendrequest
= HttpSendRequest(hResourceHandle
, nullptr, 0, nullptr, 0);
120 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);
122 if (dwError
== ERROR_INTERNET_FORCE_RETRY
)
124 else if (!httpsendrequest
)
126 DWORD err
= GetLastError();
127 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s failed: %d, %d\n"), url
, httpsendrequest
, err
);
128 InternetCloseHandle(hResourceHandle
);
129 InternetCloseHandle(hConnectHandle
);
134 DWORD contentLength
= 0;
136 DWORD length
= sizeof(contentLength
);
137 HttpQueryInfo(hResourceHandle
, HTTP_QUERY_CONTENT_LENGTH
| HTTP_QUERY_FLAG_NUMBER
, (LPVOID
)&contentLength
, &length
, NULL
);
140 DWORD statusCode
= 0;
141 DWORD length
= sizeof(statusCode
);
142 if (!HttpQueryInfo(hResourceHandle
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
, (LPVOID
)&statusCode
, &length
, NULL
) || statusCode
!= 200)
144 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s returned %d\n"), url
, statusCode
);
145 InternetCloseHandle(hResourceHandle
);
146 InternetCloseHandle(hConnectHandle
);
147 if (statusCode
== 404)
148 return ERROR_FILE_NOT_FOUND
;
149 else if (statusCode
== 403)
150 return ERROR_ACCESS_DENIED
;
151 return (DWORD
)INET_E_DOWNLOAD_FAILURE
;
155 CFile destinationFile
;
156 if (!destinationFile
.Open(dest
, CFile::modeCreate
| CFile::modeWrite
))
158 InternetCloseHandle(hResourceHandle
);
159 InternetCloseHandle(hConnectHandle
);
160 return ERROR_ACCESS_DENIED
;
162 DWORD downloadedSum
= 0; // sum of bytes downloaded so far
165 DWORD size
; // size of the data available
166 if (!InternetQueryDataAvailable(hResourceHandle
, &size
, 0, 0))
168 DWORD err
= GetLastError();
169 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s failed on InternetQueryDataAvailable: %d\n"), url
, err
);
170 InternetCloseHandle(hResourceHandle
);
171 InternetCloseHandle(hConnectHandle
);
175 DWORD downloaded
; // size of the downloaded data
176 LPTSTR lpszData
= new TCHAR
[size
+ 1];
177 if (!InternetReadFile(hResourceHandle
, (LPVOID
)lpszData
, size
, &downloaded
))
180 DWORD err
= GetLastError();
181 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download of %s failed on InternetReadFile: %d\n"), url
, err
);
182 InternetCloseHandle(hResourceHandle
);
183 InternetCloseHandle(hConnectHandle
);
193 lpszData
[downloaded
] = '\0';
194 destinationFile
.Write(lpszData
, downloaded
);
197 downloadedSum
+= downloaded
;
202 ASSERT(m_uiMsg
&& m_eventStop
);
204 if (contentLength
== 0) // got no content-length from webserver
206 DOWNLOADSTATUS downloadStatus
= { 0, 1 + 1 }; // + 1 for download of signature file
207 ::SendMessage(m_hWnd
, m_uiMsg
, 0, reinterpret_cast<LPARAM
>(&downloadStatus
));
211 if (downloadedSum
> contentLength
)
212 downloadedSum
= contentLength
- 1;
213 DOWNLOADSTATUS downloadStatus
= { downloadedSum
, contentLength
+ 1 }; // + 1 for download of signature file
214 ::SendMessage(m_hWnd
, m_uiMsg
, 0, reinterpret_cast<LPARAM
>(&downloadStatus
));
217 if (::WaitForSingleObject(*m_eventStop
, 0) == WAIT_OBJECT_0
)
219 InternetCloseHandle(hResourceHandle
);
220 InternetCloseHandle(hConnectHandle
);
221 return (DWORD
)E_ABORT
; // canceled by the user
225 destinationFile
.Close();
226 InternetCloseHandle(hResourceHandle
);
227 InternetCloseHandle(hConnectHandle
);
228 if (downloadedSum
== 0)
230 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__
) _T(": Download size of %s was zero.\n"), url
);
231 return (DWORD
)INET_E_DOWNLOAD_FAILURE
;
233 return ERROR_SUCCESS
;