diff --git a/src/help-dialog.cpp b/src/help-dialog.cpp index 5134423..869f356 100644 --- a/src/help-dialog.cpp +++ b/src/help-dialog.cpp @@ -431,3 +431,36 @@ extern "C" void forced_update_show(const char *new_version, const char *locale) obf_dash_downloads_path()); open_url(url); } + +extern "C" void ssl_error_dialog_show(const char *detail, const char *locale) +{ + bool is_de = locale && (strncmp(locale, "de", 2) == 0); + + QWidget *parent = (QWidget *)obs_frontend_get_main_window(); + + QString title = is_de ? "Verbindungsfehler" + : "Connection Error"; + + QString text = is_de + ? QString::fromUtf8( + "Easy IRL Stream konnte keine sichere Verbindung " + "zu stools.cc herstellen.\n\n" + "M\xc3\xb6""gliche Ursachen:\n" + "\xe2\x80\xa2 Antivirus-Software blockiert die Verbindung " + "(HTTPS-Scanning / SSL-Inspektion deaktivieren)\n" + "\xe2\x80\xa2 Firewall oder Proxy blockiert stools.cc\n" + "\xe2\x80\xa2 VPN-Verbindung aktiv\n\n" + "Fehler: %1") + .arg(detail && detail[0] ? detail : "SSL connect error") + : QString("Easy IRL Stream could not establish a secure " + "connection to stools.cc.\n\n" + "Possible causes:\n" + "\xe2\x80\xa2 Antivirus software blocking the connection " + "(disable HTTPS scanning / SSL inspection)\n" + "\xe2\x80\xa2 Firewall or proxy blocking stools.cc\n" + "\xe2\x80\xa2 VPN connection active\n\n" + "Error: %1") + .arg(detail && detail[0] ? detail : "SSL connect error"); + + QMessageBox::warning(parent, title, text, QMessageBox::Ok); +} diff --git a/src/help-dialog.hpp b/src/help-dialog.hpp index 07e7311..202c7e7 100644 --- a/src/help-dialog.hpp +++ b/src/help-dialog.hpp @@ -9,6 +9,7 @@ void help_dialog_show(const char *local_ip, const char *external_ip, void update_dialog_show(const char *new_version, const char *locale); void forced_update_show(const char *new_version, const char *locale); +void ssl_error_dialog_show(const char *detail, const char *locale); #ifdef __cplusplus } diff --git a/src/plugin-main.c b/src/plugin-main.c index 97454e3..23237ad 100644 --- a/src/plugin-main.c +++ b/src/plugin-main.c @@ -313,21 +313,30 @@ static bool check_update_blocking(void) if (!curl) return false; struct update_mem_buf buf = {NULL, 0}; + char errbuf[CURL_ERROR_SIZE] = ""; + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, update_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3L); curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); -#ifdef CURLSSLOPT_NATIVE_CA - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); -#endif + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); CURLcode res = curl_easy_perform(curl); long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); curl_easy_cleanup(curl); + if (res != CURLE_OK) { + blog(LOG_WARNING, "[%s] Update check failed: %s (%s)", + PLUGIN_NAME, curl_easy_strerror(res), + errbuf[0] ? errbuf : "no details"); + } + if (res != CURLE_OK || http_code != 200 || !buf.data) { free(buf.data); return false; diff --git a/src/remote-settings.c b/src/remote-settings.c index 595a7e5..402f75f 100644 --- a/src/remote-settings.c +++ b/src/remote-settings.c @@ -14,6 +14,37 @@ #include +#include "help-dialog.hpp" + +/* ---- SSL error dialog (shown once per session) ---- */ + +static volatile bool g_ssl_error_shown = false; + +struct ssl_error_ctx { + char detail[CURL_ERROR_SIZE]; +}; + +static void task_show_ssl_error(void *param) +{ + struct ssl_error_ctx *ctx = param; + ssl_error_dialog_show(ctx->detail, obs_get_locale()); + free(ctx); +} + +static void maybe_show_ssl_error(CURLcode res, const char *errbuf) +{ + if (res != CURLE_SSL_CONNECT_ERROR || g_ssl_error_shown) + return; + + g_ssl_error_shown = true; + struct ssl_error_ctx *ctx = malloc(sizeof(*ctx)); + if (ctx) { + snprintf(ctx->detail, sizeof(ctx->detail), "%s", + errbuf && errbuf[0] ? errbuf : "SSL connect error"); + obs_queue_task(OBS_TASK_UI, task_show_ssl_error, ctx, false); + } +} + /* ---- cURL helpers ---- */ struct mem_buf { @@ -53,6 +84,7 @@ static char *api_get(const char *path, const char *token) snprintf(ua, sizeof(ua), "%s%s", obf_ua_prefix(), PLUGIN_VERSION); struct mem_buf buf = {NULL, 0}; + char errbuf[CURL_ERROR_SIZE] = ""; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); @@ -60,9 +92,10 @@ static char *api_get(const char *path, const char *token) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); -#ifdef CURLSSLOPT_NATIVE_CA - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); -#endif + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); CURLcode res = curl_easy_perform(curl); long http_code = 0; @@ -71,8 +104,10 @@ static char *api_get(const char *path, const char *token) curl_easy_cleanup(curl); if (res != CURLE_OK) { - blog(LOG_WARNING, "[%s] API GET %s failed: %s", - PLUGIN_NAME, path, curl_easy_strerror(res)); + blog(LOG_WARNING, "[%s] API GET %s failed: %s (%s)", + PLUGIN_NAME, path, curl_easy_strerror(res), + errbuf[0] ? errbuf : "no details"); + maybe_show_ssl_error(res, errbuf); free(buf.data); return NULL; } @@ -105,14 +140,17 @@ static bool api_post(const char *path, const char *token, const char *json_body) char ua[128]; snprintf(ua, sizeof(ua), "%s%s", obf_ua_prefix(), PLUGIN_VERSION); + char errbuf[CURL_ERROR_SIZE] = ""; + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); curl_easy_setopt(curl, CURLOPT_USERAGENT, ua); -#ifdef CURLSSLOPT_NATIVE_CA - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); -#endif + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); CURLcode res = curl_easy_perform(curl); long http_code = 0; @@ -121,8 +159,10 @@ static bool api_post(const char *path, const char *token, const char *json_body) curl_easy_cleanup(curl); if (res != CURLE_OK) { - blog(LOG_WARNING, "[%s] API POST %s failed: %s", - PLUGIN_NAME, path, curl_easy_strerror(res)); + blog(LOG_WARNING, "[%s] API POST %s failed: %s (%s)", + PLUGIN_NAME, path, curl_easy_strerror(res), + errbuf[0] ? errbuf : "no details"); + maybe_show_ssl_error(res, errbuf); return false; } if (http_code != 200) { diff --git a/src/webhook.c b/src/webhook.c index b05c8c1..1bbc6ac 100644 --- a/src/webhook.c +++ b/src/webhook.c @@ -37,6 +37,8 @@ static void webhook_do_send(const char *url, const char *json_body) struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); + char errbuf[CURL_ERROR_SIZE] = ""; + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); @@ -44,14 +46,16 @@ static void webhook_do_send(const char *url, const char *json_body) curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, discard_response); curl_easy_setopt(curl, CURLOPT_USERAGENT, "easy-irl-stream-webhook/1.0"); -#ifdef CURLSSLOPT_NATIVE_CA - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); -#endif + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { - blog(LOG_WARNING, "[%s] Webhook failed (%s): %s", - "Easy IRL Stream", url, curl_easy_strerror(res)); + blog(LOG_WARNING, "[%s] Webhook failed (%s): %s (%s)", + "Easy IRL Stream", url, curl_easy_strerror(res), + errbuf[0] ? errbuf : "no details"); } else { long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);