Files
c3c/src/utils/http.c

136 lines
4.0 KiB
C

// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by a LGPLv3.0
// a copy of which can be found in the LICENSE file.
#include "lib.h"
#if PLATFORM_WINDOWS
#include <windows.h>
#include <winhttp.h>
static inline wchar_t *char_to_wchar(const char *str)
{
size_t size = strlen(str) + 1;
wchar_t *wc = malloc(sizeof(wchar_t) * size);
mbstowcs(wc, str, size);
return wc;
}
const char *download_file(const char *url, const char *resource, const char *file_path)
{
LPSTR pszOutBuffer;
bool results = false;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
bool is_https = memcmp("https://", url, 8) == 0;
url = url + (is_https ? 8 : 7);
// Use WinHttpOpen to obtain a session handle.
HINTERNET session = WinHttpOpen(L"C3C/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!session) error_exit("Failed to create http session.");
// Specify an HTTP server.
wchar_t *wurl = char_to_wchar(url);
HINTERNET connect = WinHttpConnect(session, wurl, is_https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT,
0);
if (!connect) error_exit("Failed to connect to '%s'", url);
free(wurl);
// Create an HTTP request handle.
wchar_t *wresource = char_to_wchar(resource);
HINTERNET request = WinHttpOpenRequest(connect, L"GET", wresource, NULL,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, is_https ? WINHTTP_FLAG_SECURE : 0);
if (!connect) error_exit("Failed to connect to '%s'.", url);
free(wresource);
FILE* file = fopen(file_path, "w+b");
if (!file) return str_printf("Failed to open file '%s' for output", file_path);
// Send a request.
bool result = WinHttpSendRequest(request, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
bool success = false;
if (!result || !WinHttpReceiveResponse(request, NULL)) goto END;
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(dwStatusCode);
if (!WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&dwStatusCode, &dwSize, WINHTTP_NO_HEADER_INDEX))
{
error_exit("Failed to get status code when requesting 'http%s://%s%s'\n",
(is_https ? "s": ""), url, resource);
}
if (dwStatusCode != 200) goto END;
char buffer[8192];
while (1)
{
DWORD dw_size = 0;
if (!WinHttpReadData(request, (LPVOID)buffer, sizeof(buffer), &dw_size)) goto END;
fwrite(buffer, (size_t)dw_size, (size_t)1, file);
if (!WinHttpQueryDataAvailable(request, &dw_size)) goto END;
if (!dw_size) break;
}
success = true;
END:
fclose(file);
WinHttpCloseHandle(request);
WinHttpCloseHandle(connect);
WinHttpCloseHandle(session);
if (!success)
{
remove(file_path);
return str_printf("Failed to retrieve '%s%s' (Status code %d).", url, resource, dwStatusCode);
}
return NULL;
}
#elif CURL_FOUND
#include <curl/curl.h>
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
return fwrite(ptr, size, nmemb, (FILE *)stream);
}
const char *download_file(const char *url, const char *resource, const char *file_path)
{
CURL *curl_handle = curl_easy_init();
if (!curl_handle) error_exit("Could not initialize cURL subsystem.");
FILE* file = fopen(file_path, "w+b");
if (!file) return str_printf("Failed to open file '%s' for output", file_path);
const char *total_url = str_printf("%s%s", url, resource);
curl_easy_setopt(curl_handle, CURLOPT_URL, total_url);
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L);
// Enable this later
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, file);
CURLcode result = curl_easy_perform(curl_handle);
if (curl_easy_perform(curl_handle) != CURLE_OK)
{
fclose(file);
remove(file_path);
return curl_easy_strerror(result);
}
fclose(file);
return NULL;
}
#endif