obs-aitum-multistream/file-updater.c
2024-06-13 17:43:54 +02:00

162 lines
3.9 KiB
C

#include "file-updater.h"
#include <curl/curl.h>
#include <obs-data.h>
#include <util/darray.h>
#include <util/dstr.h>
#include <util/platform.h>
#include <util/threading.h>
#if defined(_WIN32) && LIBCURL_VERSION_NUM >= 0x072c00
#ifdef CURLSSLOPT_REVOKE_BEST_EFFORT
#define CURL_OBS_REVOKE_SETTING CURLSSLOPT_REVOKE_BEST_EFFORT
#else
#define CURL_OBS_REVOKE_SETTING CURLSSLOPT_NO_REVOKE
#endif
#define curl_obs_set_revoke_setting(handle) curl_easy_setopt(handle, CURLOPT_SSL_OPTIONS, CURL_OBS_REVOKE_SETTING)
#else
#define curl_obs_set_revoke_setting(handle)
#endif
#define warn(msg, ...) blog(LOG_WARNING, "%s" msg, info->log_prefix, ##__VA_ARGS__)
#define info(msg, ...) blog(LOG_WARNING, "%s" msg, info->log_prefix, ##__VA_ARGS__)
struct update_info {
char error[CURL_ERROR_SIZE];
struct curl_slist *header;
DARRAY(uint8_t) file_data;
char *user_agent;
CURL *curl;
char *url;
confirm_file_callback_t callback;
void *param;
pthread_t thread;
bool thread_created;
char *log_prefix;
};
void update_info_destroy(struct update_info *info)
{
if (!info)
return;
if (info->thread_created)
pthread_join(info->thread, NULL);
da_free(info->file_data);
bfree(info->log_prefix);
bfree(info->user_agent);
bfree(info->url);
if (info->header)
curl_slist_free_all(info->header);
if (info->curl)
curl_easy_cleanup(info->curl);
bfree(info);
}
static size_t http_write(void *ptr, size_t size, size_t nmemb, void *uinfo)
{
size_t total = size * nmemb;
struct update_info *info = (struct update_info *)uinfo;
if (total)
da_push_back_array(info->file_data, ptr, total);
return total;
}
static bool do_http_request(struct update_info *info, const char *url, long *response_code)
{
CURLcode code;
uint8_t null_terminator = 0;
da_resize(info->file_data, 0);
curl_easy_setopt(info->curl, CURLOPT_URL, url);
curl_easy_setopt(info->curl, CURLOPT_HTTPHEADER, info->header);
curl_easy_setopt(info->curl, CURLOPT_ERRORBUFFER, info->error);
curl_easy_setopt(info->curl, CURLOPT_WRITEFUNCTION, http_write);
curl_easy_setopt(info->curl, CURLOPT_WRITEDATA, info);
curl_easy_setopt(info->curl, CURLOPT_FAILONERROR, true);
curl_easy_setopt(info->curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(info->curl, CURLOPT_ACCEPT_ENCODING, "");
curl_obs_set_revoke_setting(info->curl);
code = curl_easy_perform(info->curl);
if (code != CURLE_OK) {
warn("Remote update of URL \"%s\" failed: %s", url, info->error);
return false;
}
if (curl_easy_getinfo(info->curl, CURLINFO_RESPONSE_CODE, response_code) != CURLE_OK)
return false;
if (*response_code >= 400) {
warn("Remote update of URL \"%s\" failed: HTTP/%ld", url, *response_code);
return false;
}
da_push_back(info->file_data, &null_terminator);
return true;
}
struct file_update_data {
const char *name;
int version;
bool newer;
bool found;
};
static void *single_file_thread(void *data)
{
struct update_info *info = data;
struct file_download_data download_data;
long response_code;
info->curl = curl_easy_init();
if (!info->curl) {
warn("Could not initialize Curl");
return NULL;
}
if (!do_http_request(info, info->url, &response_code))
return NULL;
if (!info->file_data.array || !info->file_data.array[0])
return NULL;
download_data.name = info->url;
download_data.version = 0;
download_data.buffer.da = info->file_data.da;
info->callback(info->param, &download_data);
info->file_data.da = download_data.buffer.da;
return NULL;
}
update_info_t *update_info_create_single(const char *log_prefix, const char *user_agent, const char *file_url,
confirm_file_callback_t confirm_callback, void *param)
{
struct update_info *info;
if (!log_prefix)
log_prefix = "";
info = bzalloc(sizeof(*info));
info->log_prefix = bstrdup(log_prefix);
info->user_agent = bstrdup(user_agent);
info->url = bstrdup(file_url);
info->callback = confirm_callback;
info->param = param;
if (pthread_create(&info->thread, NULL, single_file_thread, info) == 0)
info->thread_created = true;
return info;
}