I am currently working on an application that needs to communicate between two web servers. In order to do that I am using libcurl in c. I am perfectly ok with making GET requests, but the POST ones I'm finding a bit more tricky.
For instance with curl in this case I'd do:
curl --location --request POST '%URL%' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=%scope%' \
--data-urlencode 'client_id=%client_id%' \
--data-urlencode 'client_secret=%client_secret%'
Reading the libcurl documentation I understand I need to curl_easy_setopt(curl, CURLOPT_POST, 1L); to let libcurl know I'm posting.
The only problem I have is how exactly do I make the different lines?
The fact that the CURLOPT_POSTFIELDS is in fact called "fields" rather than "field" makes me think it should support multiple fields natively, so I instinctively think
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) strlen(first_line));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, first_line);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) strlen(second_line));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, second_line);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long) strlen(third_line));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, third_line);
...and so on.
But that doesn't work and the documentation goes on saying
To make multipart/formdata posts, check out the CURLOPT_MIMEPOST option combined with curl_mime_init.
Which, since I know very little about, looks sort of scary especially looking at the example under this page.
Can anybody help me with the request I need to make or at least explain the MIME thing a little simpler?
From the mime page on curl.se I get the feeling that I should already know the things I don't know and the research I have done hasn't really shed any more light.
Since the server is expecting the data in application/x-www-form-urlencoded format, MIME doesn't apply, so ignore that.
Despite its name, CURLOPT_POSTFIELDS is not treated in a plural manner. It cannot be used in multiple calls to curl_easy_setopt() to concatenate multiple pieces of data into a single POST body. It expects the entire POST body to be specified in a single call, as per documented in CURLOPT_POSTFIELDS explained:
Pass a char * as parameter, pointing to the full data to send in an HTTP POST operation. You must make sure that the data is formatted the way you want the server to receive it. libcurl will not convert or encode it for you in any way. For example, the web server may assume that this data is URL encoded.
curl.exe concatenates multiple --data... parameters together into a single POST body:
If any of these options is used more than once on the same command line, the data pieces specified will be merged with a separating &-symbol. Thus, using '-d name=daniel -d skill=lousy' would generate a post chunk that looks like 'name=daniel&skill=lousy'.
So, you will have to do the same in your own code, eg:
// build up this string however you need to...
const char *post_body = "grant_type=client_credentials"
"&scope=<url-encoded value>"
"&client_id=<url-encoded value>"
"&client_secret=<url-encoded value>";
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_body);
// no need for CURLOPT_POSTFIELDSIZE in this case,
// libcurl will handle that internally...
Related
Tell me please, what is the main difference between options CURLOPT_MIMEPOST and CURLOPT_POSTFIELDS ?
What can be done with a CURLOPT_MIMEPOST - that cannot be done with CURLOPT_POSTFIELDS ?
CURLOPT_POSTFIELDS
Sends exactly the bytes you specify in the body of the HTTP request. With a default Content-type of application/x-www-form-urlencoded. libcurl will not add or encode the data in any way for you.
With the curl command line tool, you do this with -d.
CURLOPT_MIMEPOST
Makes libcurl send a "multipart formpost". That is a data stream using a format that allows the sender to send multiple "parts" of data to the server, each part being properly separated and identified. Each part has a name, content and its own set of headers. When an HTTP client "uploads a file", this is almost always done using multipart formposts.
Multipart formpost is structured data in the request body and this option helps you produce and send that format. An application can also produce that format by themselves should they prefer that and provide it with CURLOPT_POSTFIELDS or even using the callback CURLOPT_READFUNCTION.
With the curl command line tool, you do this with -F.
See Also
https://everything.curl.dev/libcurl-http/upload
I am successfully sending a mime message using libcurl. However, in the received email I can only see the "username" in the From: field.
curlCode_ = curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from_field);
recipients = curl_slist_append(recipients, (const char *)to_);
recipients = curl_slist_append(recipients, cc.str().c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
I assume its a security feature at gmail, ensuring that the sender is always shown in the From field.
Another issues is that I send out a multi-part mime message with an alt text portion. In zoho mail, I can view the HTML, however gmail always shows the text part.
For reference, I make sure the HTML comes before the text.
/* Build the mime message. */
mime = curl_mime_init(curl);
/* The inline part is an alternative proposing the html and the text
versions of the e-mail. */
alt = curl_mime_init(curl);
/* HTML message. */
part = curl_mime_addpart(alt);
curl_mime_data(part, html_body_.c_str(), CURL_ZERO_TERMINATED);
curl_mime_type(part, "text/html");
/* Text message. */
part = curl_mime_addpart(alt);
curl_mime_data(part, body_.c_str(), CURL_ZERO_TERMINATED);
/* Create the inline part. */
part = curl_mime_addpart(mime);
curl_mime_subparts(part, alt);
curl_mime_type(part, "multipart/alternative");
slist = curl_slist_append(NULL, "Content-Disposition: inline");
curl_mime_headers(part, slist, 1);
The code is rather long and in multiple classes, so I can post any code deemed relevant to avoid clutter. Essentially I am unsure how to debug this.
The specific problem - The text part of a multi-format MIME has to be before the html part. Now both zohomail and gmail display the html.
A more general answer relating to libcurl debugging - I use the curl program instead. Use -libcurl option to generate C code that can be used in your C/C++ project. There are sample curl commands available with most REST APIs.
I am new in quickbooks API implementation, I am always getting one error No apptoken detected; errorCode=003102; statusCode=401 when I am doing API call for customer add etc.
I am giving my steps, please look over that.
My sandbox info like that
Consumer Key: qyprdffbBBInX4a82jG73Mreyy96tC
Consumer Secret: IgpJzJrYvb9FmmdB7A0ECDGHG62Cp7dqVWjfMTvU
Access Token: qyprdlo3WrK0KhGZMTeA857AuKiVy2eaAmpXsRvG3jycYaMQ
Access Token Secret: TdPGpcUI8AiAdWFiCyb8jAAygH16bzU7VRGaspx4
I am Using PHP.
First I have generated oauth_signature.
$URL =
rawurlencode('https://sandbox-quickbooks.api.intuit.com/v3/company/408554291/customer');
$method = 'POST'; $parameter =
rawurlencode('oauth_consumer_key=qyprdffbBBInX4a82jG73Mreyy96tC&oauth_nonce=BlyqIBbv3R4T0P4qglAv1RjoYisMZk1449659733&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1449659733&oauth_token=qyprdlo3WrK0KhGZMTeA857AuKiVy2eaAmpXsRvG3jycYaMQ&oauth_version=1.0');
$ukey =
rawurlencode('IgpJzJrYvb9FmmdB7A0ECDGHG62Cp7dqVWjfMTvU').'&'.rawurlencode('TdPGpcUI8AiAdWFiCyb8jAAygH16bzU7VRGaspx4');
$hasmac = hash_hmac("sha1", $BaseURL,$ukey,1);
and My oauth_signature is jZ8JhECy/e0kpPbUdZp/o/EUC7U=
When i call API with this oauth_signature, i am getting Error 'No apptoken detected; errorCode=003102; statusCode=401'
My CURL call like this
curl -X POST -H 'Content-Type: application/json, Authorization: OAuth
oauth_token=qyprdlo3WrK0KhGZMTeA857AuKiVy2eaAmpXsRvG3jycYaMQ,oauth_consumer_key=qyprdffbBBInX4a82jG73Mreyy96tC,oauth_signature_method=HMAC-SHA1,oauth_timestamp=1449659733,oauth_version=1.0,oauth_nonce=BlyqIBbv3R4T0P4qglAv1RjoYisMZk1449659733,oauth_signature=jZ8JhECy/e0kpPbUdZp/o/EUC7U='
-d '{"data": [{"BillAddr":{"Line1":"86 A Topsia","City":"Kolkata","Country":"India","CountrySubDivisionCode":"WB","PostalCode":"700102"},"Title":"Mr.","GivenName":"ApurbaK","MiddleName":"Kumar","FamilyName":"ApurbaK","PrimaryPhone":{"FreeFormNumber":"564545465"},"PrimaryEmailAddr":{"Address":"apurbahazra12#navsoft.in"}}]}'
'https://sandbox-quickbooks.api.intuit.com/v3/company/408554291/customer'
Please look over that.
Thanks,
Apurba
Firstly, you don't want to be doing this yourself -- use a library. OAuth is complex, and implementing it yourself is going to be a hairy process, rife with errors.
Go grab a library:
https://github.com/consolibyte/quickbooks-php
Follow the quick-start guide linked there, and benefit from the examples:
https://github.com/consolibyte/quickbooks-php/tree/master/docs/partner_platform/example_app_ipp_v3
With that said, if you do decide to write it yourself, make sure you follow the OAuth spec:
https://www.rfc-editor.org/rfc/rfc5849
So far, the issues I immediately see with your implementation are:
You can't hard-code the timestamp like this: &oauth_timestamp=1449659733 The timestamp should be ever-changing, and set to the currenty timestamp.
You can't hard-code a nonce like this: &oauth_nonce=BlyqIBbv3R4T0P4qglAv1RjoYisMZk1449659733 The nonce has to change with every single request so this is going to fail after your first request.
You haven't normalized your request parameters / sorted them, per the spec.
This is the incorrect way to specify multiple headers with cURL: -H 'Content-Type: application/json, Authorization: OAuth .... Please see the cURL docs: http://curl.haxx.se/docs/manpage.html#-H
In the code you posted, you haven't actually set $BaseURL anywhere, so right now you're signing an empty string (unless you forgot to paste some code somewhere?)
What is $ukey set to? It doesn't appear to be defined in your code anywhere (did you forget to paste some code in?)
I have the following function http_send_message() wich I use each time I want to send a http message:
http_send_message(char *msg_out, char **msg_in)
{
CURLcode res;
CURL *curl;
curl = curl_easy_init();
if (!curl) return -1;
curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.133:8080/tawtaw");
curl_easy_setopt(curl, CURLOPT_USERNAME, "tawtaw");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "tawtaw");
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);
.
.
.
curl_easy_cleanup(curl);
}
But I remarked in each time the function send the http message, it try to send a request without the digest authentication header and then it send it with the digest authentication header. In normal case It should do this behaviour only in the first message. And for the subsequent messages it should remeber the authentication header and send it in each message
To obtain such a behavior you need to re-use you curl handle for the subsequent calls to take full advantage of persistent connections and Digest Access Authentication request counter:
[...] the client may make another request, reusing the server nonce value (the server only issues a new nonce for each "401" response) but providing a new client nonce (cnonce). For subsequent requests, the hexadecimal request counter (nc) must be greater than the last value it used
In practice do not clean up your curl handle. Instead maintain it and as soon as you need to perform another request:
reset it with the curl_easy_reset function: curl_easy_reset(curl);
then re-set your options.
If you use the CURLOPT_VERBOSE option you will see that for the subsequent requests you will have an Authorization header with an increasing request counter (nc=00000002, nc=00000003, etc).
I am trying to use libcurl c api to post some thing on to a site. The url works when i access it from the browser. I copied the same from the browser bar and accessing it in the program. The program hangs in the curl_easy_perform call and never returns. I also provide a write_callback but it never gets invoked.
whats wrong with this piece of code below
for privacy reasons i removed the url from the piece of code below .
int main(void)
{
CURLcode res;
CURL *curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "");
curl_easy_setopt(curl, CURLOPT_POST, 1);
//curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 1L );
//curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L );
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
res = curl_easy_perform(curl);
if (res != CURLE_OK) fprintf(stderr,"failed: %s", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return -1;
}
You probably need to set CURLOPT_POSTFIELDS and CURLOPT_POSTFIELDSIZE as well.
So if your post looks like http://awesome.com/superpost.html?blah=hi
URL should be: http://awesome.com/superpost.html
POSTFIELDS should be: blah=hi
CURLOPT_POSTFIELDSIZE should be: strlen("blah=hi")
There are some decent examples on the curl site:
basic post: http://curl.haxx.se/libcurl/c/simplepost.html
more elaborate: http://curl.haxx.se/libcurl/c/postit2.html
Note: You'll also want to escape your post field key/values with curl_escape
The problem can be withe type of url encoding. So try to see how to encode the data sent to that url proper. according to the server. in php you can use rawurlencode with is different from url-encode and may affet the server reply.