diff --git a/README.md b/README.md index 98a58d3..923f588 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,7 @@ Select [CURL options](https://curl.se/libcurl/c/curl_easy_setopt.html) are avail * [CURLOPT_CONNECTTIMEOUT](https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT.html) * [CURLOPT_CONNECTTIMEOUT_MS](https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT_MS.html) * [CURLOPT_DNS_SERVERS](https://curl.se/libcurl/c/CURLOPT_DNS_SERVERS.html) +* [CURLOPT_HTTPAUTH](https://curl.se/libcurl/c/CURLOPT_HTTPAUTH.html) * [CURLOPT_PRE_PROXY](https://curl.se/libcurl/c/CURLOPT_PRE_PROXY.html) * [CURLOPT_PROXY](https://curl.se/libcurl/c/CURLOPT_PROXY.html) * [CURLOPT_PROXYPASSWORD](https://curl.se/libcurl/c/CURLOPT_PROXYPASSWORD.html) diff --git a/expected/http.out b/expected/http.out index cb6e94a..162f341 100644 --- a/expected/http.out +++ b/expected/http.out @@ -315,3 +315,30 @@ SELECT round(extract(epoch FROM now() - start) * 10) AS m (1 row) DROP TABLE timer; +SELECT http_set_curlopt('CURLOPT_HTTPAUTH', 'CURLAUTH_NEGOTIATE'); + http_set_curlopt +------------------ + t +(1 row) + +SHOW http.CURLOPT_HTTPAUTH; + http.curlopt_httpauth +----------------------- + CURLAUTH_NEGOTIATE +(1 row) + +RESET http.CURLOPT_HTTPAUTH; +SHOW http.CURLOPT_HTTPAUTH; + http.curlopt_httpauth +----------------------- + +(1 row) + +SELECT http_set_curlopt('CURLOPT_HTTPAUTH', 'DOES_NOT_EXIST'); +ERROR: curl httpauth option 'DOES_NOT_EXIST' invalid +SHOW http.CURLOPT_HTTPAUTH; + http.curlopt_httpauth +----------------------- + +(1 row) + diff --git a/http.c b/http.c index a7d7807..08e8c58 100644 --- a/http.c +++ b/http.c @@ -143,9 +143,42 @@ typedef enum { typedef enum { CURLOPT_STRING, CURLOPT_LONG, - CURLOPT_BLOB + CURLOPT_BLOB, + CURLOPT_LONG_BITMASK } http_curlopt_type; +/* CURLOPT_HTTPAUTH string/enum value mapping */ +typedef struct { + char *str; + unsigned long val; +} http_curlopt_auth; + +/* CURLOPT_HTTPAUTH values we allow user to set at run-time */ +static http_curlopt_auth settable_curlopts_auth[] = { + { "CURLAUTH_BASIC", CURLAUTH_BASIC }, + { "CURLAUTH_DIGEST", CURLAUTH_DIGEST }, + { "CURLAUTH_NEGOTIATE", CURLAUTH_NEGOTIATE }, + { "CURLAUTH_NTLM", CURLAUTH_NTLM }, + { "CURLAUTH_ANY", CURLAUTH_ANY }, + { "CURLAUTH_ANYSAFE", CURLAUTH_ANYSAFE }, +#if LIBCURL_VERSION_NUM >= 0x071303 /* 7.19.3 */ + { "CURLAUTH_DIGEST_IE", CURLAUTH_DIGEST_IE }, +#endif +#if LIBCURL_VERSION_NUM >= 0x071503 /* 7.21.3 */ + { "CURLAUTH_ONLY", CURLAUTH_ONLY }, +#endif +#if LIBCURL_VERSION_NUM >= 0x071600 /* 7.22.0 */ + { "CURLAUTH_NTLM_WB", CURLAUTH_NTLM_WB }, +#endif +#if LIBCURL_VERSION_NUM >= 0x073D00 /* 7.61.0 */ + { "CURLAUTH_BEARER", CURLAUTH_BEARER }, +#endif +#if LIBCURL_VERSION_NUM >= 0x074a00 /* 7.74.0 */ + { "CURLAUTH_AWS_SIGV4", CURLAUTH_AWS_SIGV4 }, +#endif + { NULL, 0 }, +}; + /* CURLOPT string/enum value mapping */ typedef struct { CURLoption curlopt; @@ -156,7 +189,6 @@ typedef struct { char *curlopt_guc; } http_curlopt; - /* CURLOPT values we allow user to set at run-time */ /* Be careful adding these, as they can be a security risk */ static http_curlopt settable_curlopts[] = { @@ -171,6 +203,9 @@ static http_curlopt settable_curlopts[] = { #if LIBCURL_VERSION_NUM >= 0x070903 /* 7.9.3 */ { CURLOPT_SSLCERTTYPE, CURLOPT_STRING, false, "CURLOPT_SSLCERTTYPE", NULL, NULL }, #endif +#if LIBCURL_VERSION_NUM >= 0x070a06 /* 7.10.6 */ + { CURLOPT_HTTPAUTH, CURLOPT_LONG_BITMASK, false, "CURLOPT_HTTPAUTH", NULL, NULL }, +#endif #if LIBCURL_VERSION_NUM >= 0x070e01 /* 7.14.1 */ { CURLOPT_PROXY, CURLOPT_STRING, false, "CURLOPT_PROXY", NULL, NULL }, { CURLOPT_PROXYPORT, CURLOPT_LONG, false, "CURLOPT_PROXYPORT", NULL, NULL }, @@ -957,6 +992,39 @@ set_curlopt(CURL* handle, const http_curlopt *opt) err = curl_easy_setopt(handle, opt->curlopt, &blob); elog(DEBUG2, "pgsql-http: set '%s' to value '%s', return value = %d", opt->curlopt_guc, opt->curlopt_val, err); } +#endif +#if LIBCURL_VERSION_NUM >= 0x070a06 /* 7.10.6 */ + /* Only used for CURLOPT_HTTPAUTH */ + else if (opt->curlopt_type == CURLOPT_LONG_BITMASK) + { + if (opt->curlopt == CURLOPT_HTTPAUTH) + { + http_curlopt_auth *opt_auth = settable_curlopts_auth; + bool curlopt_httpauth_found = false; + while (opt_auth->str) + { + if (strcasecmp(opt_auth->str, opt->curlopt_val) == 0) + { + + err = curl_easy_setopt(handle, opt->curlopt, opt_auth->val); + curlopt_httpauth_found = true; + break; + } + + opt_auth++; + } + if (!curlopt_httpauth_found) + { + elog(ERROR, "invalid curl httpauth option, '%s'", opt->curlopt_val); + return false; + } + } + else + { + /* Never get here */ + elog(ERROR, "curl option '%d' is not available for run-time configuration", opt->curlopt); + } + } #endif else { @@ -1123,6 +1191,27 @@ Datum http_set_curlopt(PG_FUNCTION_ARGS) { if (strcasecmp(opt->curlopt_str, curlopt) == 0) { + if (opt->curlopt == CURLOPT_HTTPAUTH) + { + http_curlopt_auth *opt_auth = settable_curlopts_auth; + bool curlopt_httpauth_found = false; + while (opt_auth->str) + { + if (strcasecmp(opt_auth->str, value) == 0) + { + curlopt_httpauth_found = true; + break; + } + opt_auth++; + } + if (!curlopt_httpauth_found) + { + elog(ERROR, "curl httpauth option '%s' invalid", value); + PG_RETURN_BOOL(false); + } + + } + if (opt->curlopt_val) guc_free(opt->curlopt_val); opt->curlopt_val = guc_strdup(ERROR, value); PG_RETURN_BOOL(set_curlopt(handle, opt)); diff --git a/sql/http.sql b/sql/http.sql index 860d762..ff71bc7 100644 --- a/sql/http.sql +++ b/sql/http.sql @@ -209,3 +209,15 @@ SELECT * SELECT round(extract(epoch FROM now() - start) * 10) AS m FROM timer; DROP TABLE timer; + +SELECT http_set_curlopt('CURLOPT_HTTPAUTH', 'CURLAUTH_NEGOTIATE'); + +SHOW http.CURLOPT_HTTPAUTH; + +RESET http.CURLOPT_HTTPAUTH; + +SHOW http.CURLOPT_HTTPAUTH; + +SELECT http_set_curlopt('CURLOPT_HTTPAUTH', 'DOES_NOT_EXIST'); + +SHOW http.CURLOPT_HTTPAUTH;