libcurl, how to use string as a password? - c++

Following this example:
https://github.com/curl/curl/blob/master/docs/examples/smtp-tls.c
I managed to get it to work by setting the password as such:
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
However, I want to set the password up as a string, i.e.:
string myPassword = "password";
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword);
When I do this however, I get an error
curl_easy_perform() failed: Login denied
I double checked and have the program print the values of the original password and the string before it sends. They are exactly the same. I have two functions to test this on. They are almost entirely identical except that one uses a hardcoded password and the other uses the string. The hard coded one works fine, but the one that uses a string is having login issues. How could I fix this?

The CURLOPT_PASSWORD option expects a char*, not a std::string. You are passing the wrong type. The library is interpreting your data in the "wrong" way and getting nonsense.
This is not diagnosed because curl_easy_setopt uses varargs to take arbitrary parameters of varying types, depending upon the option being set. (Ideally your compiler would have rejected or at least warned about the program, but that's the cost of working with type-unsafe C libraries!)
You can pass the C-string version of your string using std::string::c_str(), like this:
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword.c_str());
There exists a project named curlpp which purports to be a type-safe wrapper around libcurl, and this might be worth a look.

It fails because curl_easy_setopt() is expecting a C style string, (i.e. char *).
What you should do in this case is convert the std::string to a C style string using .c_str().
So you should pass password by doing:
std::string myPassword = "password";
curl_easy_setopt(curl, CURLOPT_PASSWORD, myPassword.c_str());

Related

Joining a variable inside a JSON formatted post request C++

So I have this put request which submits to a service running on localhost.
Doing it the way below works just fine, note I have replaced the actual acc name with ACC_NAME and password with ACC_PASSWORD.
curl_easy_setopt(curl_account_login, CURLOPT_POSTFIELDS, "{\r\n \"username\": \ACC_NAME\,\r\n \"password\": \"ACC_PASSWORD\,\r\n \"persistLogin\": false\r\n}\r\n");
However when I wanted to pass in a variable containing the acc_name and acc_password, it does not work, I get an error response from server.
The below request is using the variable joined inside the JSON string, which gives me the error response.
curl_easy_setopt(curl_account_login, CURLOPT_POSTFIELDS, "{\r\n \"username\": \""+acc_name+"\,\r\n \"password\": \""+acc_password+"\,\r\n \"persistLogin\": false\r\n}\r\n");
I can't figure out what I am doing wrong when I am joining the string variable into the request.
It works just fine by plain, if I write the account credentials directly into the request and not in a variable.
Regards
C-style string literals (like "something") are of type const char [] (this is a null-terminated character array), which decays into a const char* (a pointer). Thus, + (as in "something" + "another") is just adding two addresses, resulting in an invalid pointer value. You cannot concatenate C-style strings by simply using + that way.
Assuming you are using C++ (not C), as indicated by your question being tagged c++, I suggest C++ string objects instead, this will allow easy concatenation.
Also, there's a note in curl documentation about CURLOPT_POSTFIELDS, mentioning that the data pointed to is not copied, thus requiring you to make sure the pointer remains valid until the associated transfer finishes. Because of that, I would prefer using CURLOPT_COPYPOSTFIELDS instead.
To sum up, do something like this:
#include <string>
// ...
std::string postfields
{
std::string{ "{" }
+ R"("username":")" + ACC_NAME
+ R"(","password":")" + ACC_PASSWORD
+ R"(","persistLogin":false})"
};
curl_easy_setopt(curl_account_login, CURLOPT_COPYPOSTFIELDS, postfields.data());

Escape url parameters for cURL

I have a url like that:
http://localhost:3000/get_agencies?zipcodecity=&zipcode=30048&city=kraków&
As you can see there city param is equal to kraków. When I pass such URL into curl I receive it somehow encoded in inappropriate way:
curl = curl_easy_init();
// Some code here
curl_easy_setopt(curl, CURLOPT_URL, url);
On the server side I get city=kraków. I tried to use curl_easy_escape(curl, url, strlen(url)); but it just encodes everything. So how can I parse only param values of a query string?
(sorry, either you significantly edited your original question, or i read it wrong the first time, let me try again)
well, i guess you can kindof repair it, guessing where the data name and value starts and ends based on the = and & characters. it's NOT foolproof, if & or ? is wrongly encoded, or if you encounter an unicode character using the equivalent bytes for their character (edit: this last part is fixable by switching to a unicode string search function), this won't be enough, but except for those 2 scenarios, something like this should work:
std::string patchInappropriatelyEncodedURL(CURL *curl, std::string url){
size_t pos=url.find("?");
size_t pos2;
if(pos==url.npos){
return url;
}
std::string ret=url.substr(0,pos+1);
std::string tmpstr;
char *escapedstr;
url=url.substr(pos+1,url.npos);
std::string type="=";
do{
pos=url.find("=");
pos2=url.find("&");
if(pos == url.npos && pos2 == url.npos){
break;
}
if(pos<pos2){
type="=";
}else{
type="&";
pos=pos2;
}
tmpstr=url.substr(0,pos);
url=url.substr(pos+1,url.npos);
escapedstr=curl_easy_escape(curl,tmpstr.c_str(),tmpstr.length());
ret.append(escapedstr);
ret.append(type);
curl_free(escapedstr);
}while(true);
escapedstr=curl_easy_escape(curl,url.c_str(),url.length());
ret.append(escapedstr);
curl_free(escapedstr);
return ret;
}
note that this function is based on guessing, and is not by any means foolproof. i suppose the guessing could improved with a dictionary for your target language or something, though.. but your time would probably be better spent on fixing the bug causing you to receive malformed urls in your program in the first place.
i deliberately omitted error checking because i'm lazy. curl_easy_escape can fail (out of memory), and when it does, it returns a nullptr. you should fix that before the code enters production, i'm too lazy.
you should put those curl_free's in a finally{} block, else you may encounter memory leaks if the string functions throw exceptions (like substr may throw bad_alloc exceptions), but again, i'm too lazy to fix it.
this is why we have curl_easy_escape.
char *escaped_string=curl_easy_escape(ch,"kraków",0);
(however, when the string is known at compile time, you could hardcode the encoded version instead of encoding it at runtime, in this case, the hardcoded version is krak%C3%B3w - your browser's javascript console can be used to figure that out, just write encodeURIComponent("kraków"); to see what the urlencoded version looks like)
gotchas:
when the 3rd paramater is 0, curl use strlen() to determine the size. this is safe when using utf8 text, but not safe with binary data. if you're encoding binary data, make sure to specify the length manually, as strlen() will stop once it finds a null byte. (other than that, curl_easy_escape, and urlencoded data is binary safe)
don't forget to curl_free(escaped_string); when you're done with it, else you'll end up with memory leaks.

libcurl outputting extra trailing bytes when downloading file

I'm having a bit of a strange problem with libcurl. Essentially, while downloading a file from an HTTP server, it's outputting some garbage bytes at the end of the file. Whereas the file should be 1,710,017 bytes, the library instead writes 1,712,128, i.e. 2,111 more. I suspect it's some sort of buffering issue, as the latter number is a multiple of 2^12 (and 2^13, but it conforms to multiples of 2^12 in other cases). The extra data is either a respective number of bytes read from another part of the file (it only seems to read from one of 4 addresses each time, all towards the end), or in one case the byte CD repeated 2,111 times.
Relevant code:
std::string url; // defined elsewhere
FILE* data; // initialized elsewhere with option "wb"
CURL* query = curl_easy_init();
curl_easy_setopt(query, CURLOPT_WRITEDATA, data);
curl_easy_setopt(query, CURLOPT_URL, url);
curl_easy_setopt(query, CURLOPT_FOLLOWLOCATION, true);
curl_easy_setopt(query, CURLOPT_SSL_VERIFYPEER, false);
CURLcode res = curl_easy_perform(query);
Also: the same issue occurs when using a simple write callback, and the issue occurs with any given remote server, not just this particular one.
Edit #1: I can only replicate this on Windows (tested on two machines with the same library files). It works on Debian.
Edit #2: It also occurs when libcurl is built on my laptop. To provide additional context, I am building from Marc Hörsken's ZIP (available from the official curl downloads page) using a VC14 environment on Windows 10.
I'm chalking this up to some weird VC bug or otherwise anomalous behavior. Rewriting the problem code to use an fstream instead of a FILE did the trick.

libcurl in C++: converting URL to some weird symbols

I have noticed that libcurl for C++ changes the URL provided to some weird symbols. Here is the code:
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
cout << "http://subdomain.mydomain.com/folder/check.php?key=" + key << endl;
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, &writeCallback);
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl_handle);
That's what I get in the console:
http://subdomain.mydomain.com/folder/check.php?key=tasdasm34234k23l423m4234mn23n4jk23bjk4b23nasdasdasdasdsdsd
* Rebuilt URL to: � ��g/
* IDN support not present, can't parse Unicode domains
* getaddrinfo(3) failed for � ��g:80
* Couldn't resolve host '� ��g'
* Closing connection 0
This code works perfectly fine when I build my project in Windows, but when I build it with Linux, this happens. If I just try to access "http://subdomain.mydomain.com/folder/check.php" with this code, it works, but as soon as I add the key, libcurl changes the whole URL.
Thanks in advance.
Like I said in my comment, the CURL library is a library of C functions, and C functions doesn't know anything about objects or classes from C++.
When you do "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key the result is a (temporary) std::string object. Passing that to a C function will not work well, and I'm surprised that the compiler actually let you pass that argument without complaining. It should have been a compiler error I think, or at the very least should give you a stern warning.
You can solve this by creating another variable to store the string object, and the use the c_str member function to get a C-style string (a pointer to constant char):
std::string url = "http://subdomain.mydomain.com/folder/check.php?key=" + addon_key;
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
I don't know if cURL copies the string, or if you need to keep the url variable alive until you're all done.
That it apparently work on Windows is nothing more than pure luck. Passing a C++ object to a function that does not expect it is undefined behavior.

libCurl : curl_easy_setopt in one method and curl_easy_perform in another does not work

I have a code, where, in one local function I use curl_easy_setopt to set the proxy URL. And in another local function I call curl_easy_perform. But when te control moves from one function to another, the proxy url set using local variable contains junk characters and the DNS query returns an error. The libcurl help page says that when we do setopt the string values is copied by the curl library. But I feel the library just referes to that value whenever it needs it. It doesn't copy the string. So if local variable is used to set proxy url, it will contain junk by the time I call curl_easy_perform.
Following is the example code snippet.
void funcSetOpt
{
char ProxyUrl[] = "someproxy";
curl_easy_setopt(curlHandle, CURLOPT_PROXY, ProxyUrl);
}
void funcPerform
{
curl_easy_perform(curlHandle);
}
That would imply that you're using a fairly old libcurl version and the following section from the curl_easy_setopt man page might affect you:
Before version 7.17.0, strings were not copied. Instead the user was
forced keep them available until libcurl no longer needed them.