Issue
I'm tying to connect to the latest Royal Mail API V4 to get a token Based on existing working code in C and libcurl to connect to V3. I have a working curl example script which gets a token using --data-urlencode which returns a token.
curl -k --location 'https://authentication.proshipping.net/connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=AAAAAAA' \
--data-urlencode 'client_secret=BBBBBBBBB' \
--data-urlencode 'grant_type=client_credentials'
I tried the following in C and libcurl
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_easy_setopt(hnd, CURLOPT_URL, "https://authentication.proshipping.net/connect/token");
curl_easy_setopt(hnd, CURLOPT_POST, TRUE);
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);
postfields = curl_slist_append(postfields, "client_id=AAAAAAA");
postfields = curl_slist_append(postfields, "client_secret=BBBBBBBBB");
postfields = curl_slist_append(postfields, "grant_type=client_credentials");
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postfields);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &s);
res = curl_easy_perform(hnd);
But the libcurl sample returns the output
{"error":"invalid_client"}
Can anyone help with --data-urlencode or equivalent command in libcurl? Thanks
Solution
There does not appear to be a single easy option that mimics the full functionality of --data-urlencode
, which includes features for loading and encoding files with a special syntax, and also concatenates its arguments to form a single blob.
The library function used to URL-encode null-terminated byte strings is curl_easy_escape
. The resulting string must be passed to curl_free
when the user is done with it.
char *curl_easy_escape(CURL *curl, const char *string, int length);
void curl_free(void *ptr);
(The internal data_urlencode
(implementing --data-urlencode
) uses curl_easy_escape
.)
As for CURLOPT_POSTFIELDS
,
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_POSTFIELDS, char *postdata);
the third argument is expected to be a null-terminated byte string (by default; can manually size with CURLOPT_POSTFIELDSIZE
) containing the full data to send.
From your example, --libcurl
generates:
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "client_id=AAAAAAA&client_secret=BBBBBBBBB&grant_type=client_credentials");
See also: http-post.c.
A vague mimicry of --data-urlencode
's most basic functionality (the name=content
syntax) would be approximately:
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int data_append(char **to, const char *name, const char *field)
{
/* 1 if initial segment, 0 if concat, justifies & below */
unsigned init = (NULL == *to);
/* first argument ignored in 7.82.0+, otherwise modify `data_append` */
char *escaped_field = curl_easy_escape(NULL, field, 0);
if (!escaped_field)
return 0;
/* previous */
size_t offset = !init ? strlen(*to) : 0;
/* name=escaped */
size_t length = strlen(name) + 1 + strlen(escaped_field);
/* previous&name=escaped\0 */
char *result = realloc(*to, offset + !init + length + 1);
if (result) {
sprintf(result + offset, "&%s=%s" + init, name, escaped_field);
*to = result;
}
curl_free(escaped_field);
return NULL != result;
}
int main(void)
{
CURL *curl = curl_easy_init();
char *data = NULL;
data_append(&data, "name", "field");
data_append(&data, "foo", "hello world!");
fprintf(stderr, "DEBUG: Sending: <%s>\n", data);
curl_easy_setopt(curl, CURLOPT_URL, "https://mockbin.org/request");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
free(data);
}
Results:
./a.out
DEBUG: Sending: <name=field&foo=hello%20world%21>
...
...
"postData": {
"mimeType": "application/x-www-form-urlencoded",
"text": "name=field&foo=hello%20world%21",
"params": {
"name": "field",
"foo": "hello world!"
}
}
...
...
This is roughly
$ curl -L 'https://mockbin.org/request' --data-urlencode 'name=field' --data-urlencode 'foo=hello world!'
(--data-urlencode
implements the ' '
=> '+'
conversion described in RFC1866.)
Aside: there is also CURLOPT_MIMEPOST
which can be used for multipart/form-data.
Answered By - Oka Answer Checked By - Willingham (WPSolving Volunteer)