http.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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, DWORD outSize)
  43. {
  44. strm->avail_in = outSize;
  45. strm->next_in = buffer;
  46. do {
  47. strm->avail_out = (uInt)zipBuf.size();
  48. strm->next_out = (Bytef *)zipBuf.data();
  49. int zret = inflate(strm, Z_NO_FLUSH);
  50. if (zret != Z_STREAM_END && zret != Z_OK)
  51. return false;
  52. try {
  53. responseBuf.append(zipBuf.data(),
  54. zipBuf.size() - strm->avail_out);
  55. } catch (...) {
  56. return false;
  57. }
  58. } while (strm->avail_out == 0);
  59. return true;
  60. }
  61. static bool ReadHTTPData(string &responseBuf, const uint8_t *buffer,
  62. DWORD outSize)
  63. {
  64. try {
  65. responseBuf.append((const char *)buffer, outSize);
  66. } catch (...) {
  67. return false;
  68. }
  69. return true;
  70. }
  71. bool HTTPPostData(const wchar_t *url,
  72. const BYTE * data,
  73. int dataLen,
  74. const wchar_t *extraHeaders,
  75. int * responseCode,
  76. string & responseBuf)
  77. {
  78. HttpHandle hSession;
  79. HttpHandle hConnect;
  80. HttpHandle hRequest;
  81. string zipBuf;
  82. URL_COMPONENTS urlComponents = {};
  83. bool secure = false;
  84. wchar_t hostName[256];
  85. wchar_t path[1024];
  86. const wchar_t *acceptTypes[] = {L"*/*", nullptr};
  87. const DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
  88. responseBuf.clear();
  89. /* -------------------------------------- *
  90. * get URL components */
  91. urlComponents.dwStructSize = sizeof(urlComponents);
  92. urlComponents.lpszHostName = hostName;
  93. urlComponents.dwHostNameLength = _countof(hostName);
  94. urlComponents.lpszUrlPath = path;
  95. urlComponents.dwUrlPathLength = _countof(path);
  96. WinHttpCrackUrl(url, 0, 0, &urlComponents);
  97. if (urlComponents.nPort == 443)
  98. secure = true;
  99. /* -------------------------------------- *
  100. * connect to server */
  101. hSession = WinHttpOpen(L"OBS Studio Updater/2.1",
  102. WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
  103. WINHTTP_NO_PROXY_NAME,
  104. WINHTTP_NO_PROXY_BYPASS,
  105. 0);
  106. if (!hSession) {
  107. *responseCode = -1;
  108. return false;
  109. }
  110. WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
  111. (LPVOID)&tlsProtocols, sizeof(tlsProtocols));
  112. hConnect = WinHttpConnect(hSession, hostName,
  113. secure
  114. ? INTERNET_DEFAULT_HTTPS_PORT
  115. : INTERNET_DEFAULT_HTTP_PORT, 0);
  116. if (!hConnect) {
  117. *responseCode = -2;
  118. return false;
  119. }
  120. /* -------------------------------------- *
  121. * request data */
  122. hRequest = WinHttpOpenRequest(hConnect,
  123. L"POST",
  124. path,
  125. nullptr,
  126. WINHTTP_NO_REFERER,
  127. acceptTypes,
  128. secure
  129. ? WINHTTP_FLAG_SECURE |
  130. WINHTTP_FLAG_REFRESH
  131. : WINHTTP_FLAG_REFRESH);
  132. if (!hRequest) {
  133. *responseCode = -3;
  134. return false;
  135. }
  136. bool bResults = !!WinHttpSendRequest(hRequest, extraHeaders,
  137. extraHeaders ? -1 : 0,
  138. (void *)data, dataLen, dataLen, 0);
  139. /* -------------------------------------- *
  140. * end request */
  141. if (bResults) {
  142. bResults = !!WinHttpReceiveResponse(hRequest, nullptr);
  143. } else {
  144. *responseCode = GetLastError();
  145. return false;
  146. }
  147. /* -------------------------------------- *
  148. * get headers */
  149. wchar_t encoding[64];
  150. DWORD encodingLen;
  151. wchar_t statusCode[8];
  152. DWORD statusCodeLen;
  153. statusCodeLen = sizeof(statusCode);
  154. if (!WinHttpQueryHeaders(hRequest,
  155. WINHTTP_QUERY_STATUS_CODE,
  156. WINHTTP_HEADER_NAME_BY_INDEX,
  157. &statusCode,
  158. &statusCodeLen,
  159. WINHTTP_NO_HEADER_INDEX)) {
  160. *responseCode = -4;
  161. return false;
  162. } else {
  163. statusCode[_countof(statusCode) - 1] = 0;
  164. }
  165. encodingLen = sizeof(encoding);
  166. if (!WinHttpQueryHeaders(hRequest,
  167. WINHTTP_QUERY_CONTENT_ENCODING,
  168. WINHTTP_HEADER_NAME_BY_INDEX,
  169. encoding,
  170. &encodingLen,
  171. WINHTTP_NO_HEADER_INDEX)) {
  172. encoding[0] = 0;
  173. if (GetLastError() != ERROR_WINHTTP_HEADER_NOT_FOUND) {
  174. *responseCode = -5;
  175. return false;
  176. }
  177. } else {
  178. encoding[_countof(encoding) - 1] = 0;
  179. }
  180. /* -------------------------------------- *
  181. * allocate response data */
  182. DWORD responseBufSize = MAX_BUF_SIZE;
  183. try {
  184. responseBuf.reserve(responseBufSize);
  185. } catch (...) {
  186. *responseCode = -6;
  187. return false;
  188. }
  189. /* -------------------------------------- *
  190. * if zipped, initialize zip data */
  191. ZipStream strm;
  192. bool gzip = wcscmp(encoding, L"gzip") == 0;
  193. if (gzip) {
  194. strm->zalloc = Z_NULL;
  195. strm->zfree = Z_NULL;
  196. strm->opaque = Z_NULL;
  197. strm->avail_in = 0;
  198. strm->next_in = Z_NULL;
  199. if (!strm.inflate())
  200. return false;
  201. try {
  202. zipBuf.resize(MAX_BUF_SIZE);
  203. } catch (...) {
  204. *responseCode = -6;
  205. return false;
  206. }
  207. }
  208. /* -------------------------------------- *
  209. * read data */
  210. *responseCode = wcstoul(statusCode, nullptr, 10);
  211. /* are we supposed to return true here? */
  212. if (!bResults || *responseCode != 200)
  213. return true;
  214. BYTE buffer[READ_BUF_SIZE];
  215. DWORD dwSize, outSize;
  216. do {
  217. /* Check for available data. */
  218. dwSize = 0;
  219. if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
  220. *responseCode = -8;
  221. return false;
  222. }
  223. dwSize = std::min(dwSize, (DWORD)sizeof(buffer));
  224. if (!WinHttpReadData(hRequest, (void *)buffer, dwSize,
  225. &outSize)) {
  226. *responseCode = -9;
  227. return false;
  228. }
  229. if (!outSize)
  230. break;
  231. if (gzip) {
  232. if (!ReadZippedHTTPData(responseBuf, strm, zipBuf,
  233. buffer, outSize)) {
  234. *responseCode = -6;
  235. return false;
  236. }
  237. } else {
  238. if (!ReadHTTPData(responseBuf, buffer, outSize)) {
  239. *responseCode = -6;
  240. return false;
  241. }
  242. }
  243. if (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {
  244. *responseCode = -14;
  245. return false;
  246. }
  247. } while (dwSize > 0);
  248. return true;
  249. }
  250. /* ------------------------------------------------------------------------ */
  251. static bool ReadHTTPZippedFile(z_stream *strm, HANDLE updateFile,
  252. string &zipBuf, const uint8_t *buffer, DWORD outSize,
  253. int *responseCode)
  254. {
  255. strm->avail_in = outSize;
  256. strm->next_in = buffer;
  257. do {
  258. strm->avail_out = (uInt)zipBuf.size();
  259. strm->next_out = (Bytef *)zipBuf.data();
  260. int zret = inflate(strm, Z_NO_FLUSH);
  261. if (zret != Z_STREAM_END && zret != Z_OK)
  262. return false;
  263. DWORD written;
  264. if (!WriteFile(updateFile,
  265. zipBuf.data(),
  266. MAX_BUF_SIZE - strm->avail_out,
  267. &written,
  268. nullptr)) {
  269. *responseCode = -10;
  270. return false;
  271. }
  272. if (written != MAX_BUF_SIZE - strm->avail_out) {
  273. *responseCode = -11;
  274. return false;
  275. }
  276. completedFileSize += written;
  277. } while (strm->avail_out == 0);
  278. return true;
  279. }
  280. static bool ReadHTTPFile(HANDLE updateFile, const uint8_t *buffer,
  281. DWORD outSize, int *responseCode)
  282. {
  283. DWORD written;
  284. if (!WriteFile(updateFile, buffer, outSize, &written, nullptr)) {
  285. *responseCode = -12;
  286. return false;
  287. }
  288. if (written != outSize) {
  289. *responseCode = -13;
  290. return false;
  291. }
  292. completedFileSize += outSize;
  293. return true;
  294. }
  295. bool HTTPGetFile(HINTERNET hConnect,
  296. const wchar_t *url,
  297. const wchar_t *outputPath,
  298. const wchar_t *extraHeaders,
  299. int * responseCode)
  300. {
  301. HttpHandle hRequest;
  302. const wchar_t *acceptTypes[] = {L"*/*", nullptr};
  303. URL_COMPONENTS urlComponents = {};
  304. bool secure = false;
  305. string zipBuf;
  306. wchar_t hostName[256];
  307. wchar_t path[1024];
  308. /* -------------------------------------- *
  309. * get URL components */
  310. urlComponents.dwStructSize = sizeof(urlComponents);
  311. urlComponents.lpszHostName = hostName;
  312. urlComponents.dwHostNameLength = _countof(hostName);
  313. urlComponents.lpszUrlPath = path;
  314. urlComponents.dwUrlPathLength = _countof(path);
  315. WinHttpCrackUrl(url, 0, 0, &urlComponents);
  316. if (urlComponents.nPort == 443)
  317. secure = true;
  318. /* -------------------------------------- *
  319. * request data */
  320. hRequest = WinHttpOpenRequest(hConnect,
  321. L"GET",
  322. path,
  323. nullptr,
  324. WINHTTP_NO_REFERER,
  325. acceptTypes,
  326. secure
  327. ? WINHTTP_FLAG_SECURE |
  328. WINHTTP_FLAG_REFRESH
  329. : WINHTTP_FLAG_REFRESH);
  330. if (!hRequest) {
  331. *responseCode = -3;
  332. return false;
  333. }
  334. bool bResults = !!WinHttpSendRequest(hRequest, extraHeaders,
  335. extraHeaders ? -1 : 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
  336. /* -------------------------------------- *
  337. * end request */
  338. if (bResults) {
  339. bResults = !!WinHttpReceiveResponse(hRequest, nullptr);
  340. } else {
  341. *responseCode = GetLastError();
  342. return false;
  343. }
  344. /* -------------------------------------- *
  345. * get headers */
  346. wchar_t encoding[64];
  347. DWORD encodingLen;
  348. wchar_t statusCode[8];
  349. DWORD statusCodeLen;
  350. statusCodeLen = sizeof(statusCode);
  351. if (!WinHttpQueryHeaders(hRequest,
  352. WINHTTP_QUERY_STATUS_CODE,
  353. WINHTTP_HEADER_NAME_BY_INDEX,
  354. &statusCode,
  355. &statusCodeLen,
  356. WINHTTP_NO_HEADER_INDEX)) {
  357. *responseCode = -4;
  358. return false;
  359. } else {
  360. statusCode[_countof(statusCode) - 1] = 0;
  361. }
  362. encodingLen = sizeof(encoding);
  363. if (!WinHttpQueryHeaders(hRequest,
  364. WINHTTP_QUERY_CONTENT_ENCODING,
  365. WINHTTP_HEADER_NAME_BY_INDEX,
  366. encoding,
  367. &encodingLen,
  368. WINHTTP_NO_HEADER_INDEX)) {
  369. encoding[0] = 0;
  370. if (GetLastError() != ERROR_WINHTTP_HEADER_NOT_FOUND) {
  371. *responseCode = -5;
  372. return false;
  373. }
  374. } else {
  375. encoding[_countof(encoding) - 1] = 0;
  376. }
  377. /* -------------------------------------- *
  378. * allocate response data */
  379. ZipStream strm;
  380. bool gzip = wcscmp(encoding, L"gzip") == 0;
  381. if (gzip) {
  382. strm->zalloc = Z_NULL;
  383. strm->zfree = Z_NULL;
  384. strm->opaque = Z_NULL;
  385. strm->avail_in = 0;
  386. strm->next_in = Z_NULL;
  387. if (!strm.inflate())
  388. return false;
  389. try {
  390. zipBuf.resize(MAX_BUF_SIZE);
  391. } catch (...) {
  392. *responseCode = -6;
  393. return false;
  394. }
  395. }
  396. /* -------------------------------------- *
  397. * read data */
  398. *responseCode = wcstoul(statusCode, nullptr, 10);
  399. /* are we supposed to return true here? */
  400. if (!bResults || *responseCode != 200)
  401. return true;
  402. BYTE buffer[READ_BUF_SIZE];
  403. DWORD dwSize, outSize;
  404. int lastPosition = 0;
  405. WinHandle updateFile = CreateFile(outputPath, GENERIC_WRITE, 0,
  406. nullptr, CREATE_ALWAYS, 0, nullptr);
  407. if (!updateFile.Valid()) {
  408. *responseCode = -7;
  409. return false;
  410. }
  411. do {
  412. /* Check for available data. */
  413. dwSize = 0;
  414. if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {
  415. *responseCode = -8;
  416. return false;
  417. }
  418. dwSize = std::min(dwSize, (DWORD)sizeof(buffer));
  419. if (!WinHttpReadData(hRequest, (void *)buffer, dwSize,
  420. &outSize)) {
  421. *responseCode = -9;
  422. return false;
  423. } else {
  424. if (!outSize)
  425. break;
  426. if (gzip) {
  427. if (!ReadHTTPZippedFile(strm, updateFile,
  428. zipBuf, buffer,
  429. outSize, responseCode))
  430. return false;
  431. } else {
  432. if (!ReadHTTPFile(updateFile, buffer,
  433. outSize, responseCode))
  434. return false;
  435. }
  436. int position = (int)(((float)completedFileSize /
  437. (float)totalFileSize) * 100.0f);
  438. if (position > lastPosition) {
  439. lastPosition = position;
  440. SendDlgItemMessage(hwndMain, IDC_PROGRESS,
  441. PBM_SETPOS, position, 0);
  442. }
  443. }
  444. if (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {
  445. *responseCode = -14;
  446. return false;
  447. }
  448. } while (dwSize > 0);
  449. return true;
  450. }