http.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. * Copyright (c) 2017-2018 Hugh Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "Updater.hpp"
  17. #include <algorithm>
  18. using namespace std;
  19. #define MAX_BUF_SIZE 262144
  20. #define READ_BUF_SIZE 32768
  21. /* ------------------------------------------------------------------------ */
  22. class ZipStream {
  23. z_stream strm = {};
  24. bool initialized = false;
  25. public:
  26. inline ~ZipStream()
  27. {
  28. if (initialized)
  29. inflateEnd(&strm);
  30. }
  31. inline operator z_stream *() { return &strm; }
  32. inline z_stream *operator->() { return &strm; }
  33. inline bool inflate()
  34. {
  35. int ret = inflateInit2(&strm, 16 + MAX_WBITS);
  36. initialized = (ret == Z_OK);
  37. return initialized;
  38. }
  39. };
  40. /* ------------------------------------------------------------------------ */
  41. static bool ReadZippedHTTPData(string &responseBuf, z_stream *strm,
  42. string &zipBuf, const uint8_t *buffer,
  43. DWORD outSize)
  44. {
  45. strm->avail_in = outSize;
  46. strm->next_in = buffer;
  47. do {
  48. strm->avail_out = (uInt)zipBuf.size();
  49. strm->next_out = (Bytef *)zipBuf.data();
  50. int zret = inflate(strm, Z_NO_FLUSH);
  51. if (zret != Z_STREAM_END && zret != Z_OK)
  52. return false;
  53. try {
  54. responseBuf.append(zipBuf.data(),
  55. zipBuf.size() - strm->avail_out);
  56. } catch (...) {
  57. return false;
  58. }
  59. } while (strm->avail_out == 0);
  60. return true;
  61. }
  62. static bool ReadHTTPData(string &responseBuf, const uint8_t *buffer,
  63. DWORD outSize)
  64. {
  65. try {
  66. responseBuf.append((const char *)buffer, outSize);
  67. } catch (...) {
  68. return false;
  69. }
  70. return true;
  71. }
  72. bool HTTPPostData(const wchar_t *url, const BYTE *data, int dataLen,
  73. const wchar_t *extraHeaders, int *responseCode,
  74. string &responseBuf)
  75. {
  76. HttpHandle hSession;
  77. HttpHandle hConnect;
  78. HttpHandle hRequest;
  79. string zipBuf;
  80. URL_COMPONENTS urlComponents = {};
  81. bool secure = false;
  82. wchar_t hostName[256];
  83. wchar_t path[1024];
  84. const wchar_t *acceptTypes[] = {L"*/*", nullptr};
  85. const DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
  86. responseBuf.clear();
  87. /* -------------------------------------- *
  88. * get URL components */
  89. urlComponents.dwStructSize = sizeof(urlComponents);
  90. urlComponents.lpszHostName = hostName;
  91. urlComponents.dwHostNameLength = _countof(hostName);
  92. urlComponents.lpszUrlPath = path;
  93. urlComponents.dwUrlPathLength = _countof(path);
  94. WinHttpCrackUrl(url, 0, 0, &urlComponents);
  95. if (urlComponents.nPort == 443)
  96. secure = true;
  97. /* -------------------------------------- *
  98. * connect to server */
  99. hSession = WinHttpOpen(L"OBS Studio Updater/2.1",
  100. WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
  101. WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS,
  102. 0);
  103. if (!hSession) {
  104. *responseCode = -1;
  105. return false;
  106. }
  107. WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
  108. (LPVOID)&tlsProtocols, sizeof(tlsProtocols));
  109. hConnect = WinHttpConnect(hSession, hostName,
  110. secure ? INTERNET_DEFAULT_HTTPS_PORT
  111. : INTERNET_DEFAULT_HTTP_PORT,
  112. 0);
  113. if (!hConnect) {
  114. *responseCode = -2;
  115. return false;
  116. }
  117. /* -------------------------------------- *
  118. * request data */
  119. hRequest = WinHttpOpenRequest(hConnect, L"POST", path, nullptr,
  120. WINHTTP_NO_REFERER, acceptTypes,
  121. secure ? WINHTTP_FLAG_SECURE |
  122. WINHTTP_FLAG_REFRESH
  123. : WINHTTP_FLAG_REFRESH);
  124. if (!hRequest) {
  125. *responseCode = -3;
  126. return false;
  127. }
  128. bool bResults = !!WinHttpSendRequest(hRequest, extraHeaders,
  129. extraHeaders ? -1 : 0,
  130. (void *)data, dataLen, dataLen, 0);
  131. /* -------------------------------------- *
  132. * end request */
  133. if (bResults) {
  134. bResults = !!WinHttpReceiveResponse(hRequest, nullptr);
  135. } else {
  136. *responseCode = GetLastError();
  137. return false;
  138. }
  139. /* -------------------------------------- *
  140. * get headers */
  141. wchar_t encoding[64];
  142. DWORD encodingLen;
  143. wchar_t statusCode[8];
  144. DWORD statusCodeLen;
  145. statusCodeLen = sizeof(statusCode);
  146. if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE,
  147. WINHTTP_HEADER_NAME_BY_INDEX, &statusCode,
  148. &statusCodeLen, WINHTTP_NO_HEADER_INDEX)) {
  149. *responseCode = -4;
  150. return false;
  151. } else {
  152. statusCode[_countof(statusCode) - 1] = 0;
  153. }
  154. encodingLen = sizeof(encoding);
  155. if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_ENCODING,
  156. WINHTTP_HEADER_NAME_BY_INDEX, encoding,
  157. &encodingLen, WINHTTP_NO_HEADER_INDEX)) {
  158. encoding[0] = 0;
  159. if (GetLastError() != ERROR_WINHTTP_HEADER_NOT_FOUND) {
  160. *responseCode = -5;
  161. return false;
  162. }
  163. } else {
  164. encoding[_countof(encoding) - 1] = 0;
  165. }
  166. /* -------------------------------------- *
  167. * allocate response data */
  168. DWORD responseBufSize = MAX_BUF_SIZE;
  169. try {
  170. responseBuf.reserve(responseBufSize);
  171. } catch (...) {
  172. *responseCode = -6;
  173. return false;
  174. }
  175. /* -------------------------------------- *
  176. * if zipped, initialize zip data */
  177. ZipStream strm;
  178. bool gzip = wcscmp(encoding, L"gzip") == 0;
  179. if (gzip) {
  180. strm->zalloc = Z_NULL;
  181. strm->zfree = Z_NULL;
  182. strm->opaque = Z_NULL;
  183. strm->avail_in = 0;
  184. strm->next_in = Z_NULL;
  185. if (!strm.inflate())
  186. return false;
  187. try {
  188. zipBuf.resize(MAX_BUF_SIZE);
  189. } catch (...) {
  190. *responseCode = -6;
  191. return false;
  192. }
  193. }
  194. /* -------------------------------------- *
  195. * read data */
  196. *responseCode = wcstoul(statusCode, nullptr, 10);
  197. /* are we supposed to return true here? */
  198. if (!bResults || *responseCode != 200)
  199. return true;
  200. BYTE buffer[READ_BUF_SIZE];
  201. DWORD dwSize, outSize;
  202. do {
  203. /* Check for available data. */
  204. dwSize = 0;
  205. if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
  206. *responseCode = -8;
  207. return false;
  208. }
  209. dwSize = std::min(dwSize, (DWORD)sizeof(buffer));
  210. if (!WinHttpReadData(hRequest, (void *)buffer, dwSize,
  211. &outSize)) {
  212. *responseCode = -9;
  213. return false;
  214. }
  215. if (!outSize)
  216. break;
  217. if (gzip) {
  218. if (!ReadZippedHTTPData(responseBuf, strm, zipBuf,
  219. buffer, outSize)) {
  220. *responseCode = -6;
  221. return false;
  222. }
  223. } else {
  224. if (!ReadHTTPData(responseBuf, buffer, outSize)) {
  225. *responseCode = -6;
  226. return false;
  227. }
  228. }
  229. if (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {
  230. *responseCode = -14;
  231. return false;
  232. }
  233. } while (dwSize > 0);
  234. return true;
  235. }
  236. /* ------------------------------------------------------------------------ */
  237. static bool ReadHTTPZippedFile(z_stream *strm, HANDLE updateFile,
  238. string &zipBuf, const uint8_t *buffer,
  239. DWORD outSize, int *responseCode)
  240. {
  241. strm->avail_in = outSize;
  242. strm->next_in = buffer;
  243. do {
  244. strm->avail_out = (uInt)zipBuf.size();
  245. strm->next_out = (Bytef *)zipBuf.data();
  246. int zret = inflate(strm, Z_NO_FLUSH);
  247. if (zret != Z_STREAM_END && zret != Z_OK)
  248. return false;
  249. DWORD written;
  250. if (!WriteFile(updateFile, zipBuf.data(),
  251. MAX_BUF_SIZE - strm->avail_out, &written,
  252. nullptr)) {
  253. *responseCode = -10;
  254. return false;
  255. }
  256. if (written != MAX_BUF_SIZE - strm->avail_out) {
  257. *responseCode = -11;
  258. return false;
  259. }
  260. completedFileSize += written;
  261. } while (strm->avail_out == 0);
  262. return true;
  263. }
  264. static bool ReadHTTPFile(HANDLE updateFile, const uint8_t *buffer,
  265. DWORD outSize, int *responseCode)
  266. {
  267. DWORD written;
  268. if (!WriteFile(updateFile, buffer, outSize, &written, nullptr)) {
  269. *responseCode = -12;
  270. return false;
  271. }
  272. if (written != outSize) {
  273. *responseCode = -13;
  274. return false;
  275. }
  276. completedFileSize += outSize;
  277. return true;
  278. }
  279. bool HTTPGetFile(HINTERNET hConnect, const wchar_t *url,
  280. const wchar_t *outputPath, const wchar_t *extraHeaders,
  281. int *responseCode)
  282. {
  283. HttpHandle hRequest;
  284. const wchar_t *acceptTypes[] = {L"*/*", nullptr};
  285. URL_COMPONENTS urlComponents = {};
  286. bool secure = false;
  287. string zipBuf;
  288. wchar_t hostName[256];
  289. wchar_t path[1024];
  290. /* -------------------------------------- *
  291. * get URL components */
  292. urlComponents.dwStructSize = sizeof(urlComponents);
  293. urlComponents.lpszHostName = hostName;
  294. urlComponents.dwHostNameLength = _countof(hostName);
  295. urlComponents.lpszUrlPath = path;
  296. urlComponents.dwUrlPathLength = _countof(path);
  297. WinHttpCrackUrl(url, 0, 0, &urlComponents);
  298. if (urlComponents.nPort == 443)
  299. secure = true;
  300. /* -------------------------------------- *
  301. * request data */
  302. hRequest = WinHttpOpenRequest(hConnect, L"GET", path, nullptr,
  303. WINHTTP_NO_REFERER, acceptTypes,
  304. secure ? WINHTTP_FLAG_SECURE |
  305. WINHTTP_FLAG_REFRESH
  306. : WINHTTP_FLAG_REFRESH);
  307. if (!hRequest) {
  308. *responseCode = -3;
  309. return false;
  310. }
  311. bool bResults = !!WinHttpSendRequest(hRequest, extraHeaders,
  312. extraHeaders ? -1 : 0,
  313. WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
  314. /* -------------------------------------- *
  315. * end request */
  316. if (bResults) {
  317. bResults = !!WinHttpReceiveResponse(hRequest, nullptr);
  318. } else {
  319. *responseCode = GetLastError();
  320. return false;
  321. }
  322. /* -------------------------------------- *
  323. * get headers */
  324. wchar_t encoding[64];
  325. DWORD encodingLen;
  326. wchar_t statusCode[8];
  327. DWORD statusCodeLen;
  328. statusCodeLen = sizeof(statusCode);
  329. if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE,
  330. WINHTTP_HEADER_NAME_BY_INDEX, &statusCode,
  331. &statusCodeLen, WINHTTP_NO_HEADER_INDEX)) {
  332. *responseCode = -4;
  333. return false;
  334. } else {
  335. statusCode[_countof(statusCode) - 1] = 0;
  336. }
  337. encodingLen = sizeof(encoding);
  338. if (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_ENCODING,
  339. WINHTTP_HEADER_NAME_BY_INDEX, encoding,
  340. &encodingLen, WINHTTP_NO_HEADER_INDEX)) {
  341. encoding[0] = 0;
  342. if (GetLastError() != ERROR_WINHTTP_HEADER_NOT_FOUND) {
  343. *responseCode = -5;
  344. return false;
  345. }
  346. } else {
  347. encoding[_countof(encoding) - 1] = 0;
  348. }
  349. /* -------------------------------------- *
  350. * allocate response data */
  351. ZipStream strm;
  352. bool gzip = wcscmp(encoding, L"gzip") == 0;
  353. if (gzip) {
  354. strm->zalloc = Z_NULL;
  355. strm->zfree = Z_NULL;
  356. strm->opaque = Z_NULL;
  357. strm->avail_in = 0;
  358. strm->next_in = Z_NULL;
  359. if (!strm.inflate())
  360. return false;
  361. try {
  362. zipBuf.resize(MAX_BUF_SIZE);
  363. } catch (...) {
  364. *responseCode = -6;
  365. return false;
  366. }
  367. }
  368. /* -------------------------------------- *
  369. * read data */
  370. *responseCode = wcstoul(statusCode, nullptr, 10);
  371. /* are we supposed to return true here? */
  372. if (!bResults || *responseCode != 200)
  373. return true;
  374. BYTE buffer[READ_BUF_SIZE];
  375. DWORD dwSize, outSize;
  376. int lastPosition = 0;
  377. WinHandle updateFile = CreateFile(outputPath, GENERIC_WRITE, 0, nullptr,
  378. CREATE_ALWAYS, 0, nullptr);
  379. if (!updateFile.Valid()) {
  380. *responseCode = -7;
  381. return false;
  382. }
  383. do {
  384. /* Check for available data. */
  385. dwSize = 0;
  386. if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
  387. *responseCode = -8;
  388. return false;
  389. }
  390. dwSize = std::min(dwSize, (DWORD)sizeof(buffer));
  391. if (!WinHttpReadData(hRequest, (void *)buffer, dwSize,
  392. &outSize)) {
  393. *responseCode = -9;
  394. return false;
  395. } else {
  396. if (!outSize)
  397. break;
  398. if (gzip) {
  399. if (!ReadHTTPZippedFile(strm, updateFile,
  400. zipBuf, buffer, outSize,
  401. responseCode))
  402. return false;
  403. } else {
  404. if (!ReadHTTPFile(updateFile, buffer, outSize,
  405. responseCode))
  406. return false;
  407. }
  408. int position = (int)(((float)completedFileSize /
  409. (float)totalFileSize) *
  410. 100.0f);
  411. if (position > lastPosition) {
  412. lastPosition = position;
  413. SendDlgItemMessage(hwndMain, IDC_PROGRESS,
  414. PBM_SETPOS, position, 0);
  415. }
  416. }
  417. if (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {
  418. *responseCode = -14;
  419. return false;
  420. }
  421. } while (dwSize > 0);
  422. return true;
  423. }