I would like to convert this code (using a scripting language with a Sockets extension) over to C++ using LibCurl. I have only used LibCurl once previously, so I am at a bit of a loss as to what else I am going to need. My main point of confusing is wether I should be able to just use curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); to send this, or if I am going to need to extract the socket, then send it over that.
Here is the relevant snippet from the script...
public OnSocketConnected(Handle:socket, any:friendId)
{
decl String:CommunityId[32];
FriendIDToCommunityId(friendId, CommunityId, sizeof(CommunityId));
decl String:query[2048];
decl String:cookieString[100];
decl String:inviterString[32];
decl String:groupString[32];
GetConVarString(cookie, cookieString, sizeof(cookieString));
GetConVarString(inviter, inviterString, sizeof(inviterString));
GetConVarString(group, groupString, sizeof(groupString));
Format(query, sizeof(query), "GET /actions/GroupInvite?type=groupInvite&inviter=%s&invitee=%s&group=%s HTTP/1.1\r\nHost: steamcommunity.com\r\nConnection: close\r\nCookie: steamLogin=%s\r\n\r\n", inviterString, CommunityId, groupString, cookieString);
SocketSend(socket, query);
LogMessage("%s", query);
}
and here is what I have in C++ so far. It is looking like I am going to need to extract the socket first, but I am not proficient with network coding, so I am not exactly sure where I need to go from here.
void InviteToGroup(const char *szUserSteamID, const char *szInviterSteamID, const char *steamUser, const char *steamPass)
{
CURL *curl;
CURLcode res, result;
//int sockfd; /* socket */
char errorBuffer[CURL_ERROR_SIZE];
const char *szUserID = GetCommunityID(szUserSteamID); // User's Steam Community ID
const char *szInviterID = GetCommunityID(szInviterSteamID); // Inviter's Steam Community ID
char *szGroupID = "";
GetGroupCommunityID(1254745, &szGroupID); // Group Steam Community ID
const char *szCookie = "76561198018111441%7C%7CC7D70E74A3F592F3E130CCF4CAACD4A7B9CAD993"; // Steam Community Login Cookie
char *buffer = new char[512];
// Create the GET request
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "GET /actions/GroupInvite?type=groupInvite&inviter=");
snprintf(buffer, sizeof(buffer), "%s&invitee=%s&group=%s ", szInviterID, szUserID, szGroupID);
headers = curl_slist_append(headers, buffer);
headers = curl_slist_append(headers, "HTTP/1.1\r\nHost: steamcommunity.com\r\nConnection: close\r\nCookie: ");
snprintf(buffer, sizeof(buffer), "steamLogin=%s\r\n\r\n", szCookie);
headers = curl_slist_append(headers, buffer);
delete buffer;
// Init CURL
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "https://www.steamcommunity.com");
curl_easy_setopt(curl, CURLOPT_PORT, 443); // Check this before using
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
//curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1); // No transfer, just extract the socket
// Find out if we need to use Proxy stuff here
char *userpass = new char[64];
snprintf(userpass, sizeof(userpass), "%s:%s", steamUser, steamPass);
curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
// Attempt to Connect the Steam Community Server
res = curl_easy_perform(curl);
if (res == CURLE_OK)
Msg("Connected Successfully!\n");
else
Msg("Connection Failed! Error: %s\n", errorBuffer);
// Close the connection
curl_easy_cleanup(curl);
}
}
So corrections here as your C++ is incorrect which will mean your code won't work.
sizeof( char * ) is the size of a pointer, not the length of the string. So your snprintf will print up to 4 characters on a 32-bit system which will include the null terminator so you will actually get only a string of length 3.
If you are going to use a "should be big enough" buffer do not allocate it with new. You could use std::vector if you want to use snprintf, then check the return value of snprintf which is the length of the actual string that would be written and then increase the buffer size if necessary (to the return value + 1).
vector<char> buffer;
size_t required = 511;
do
{
buffer.resize( required + 1 );
required = snprintf( &buffer[0], buffer.size(), formatstr, ... );
}
while( required >= buffer.size() );
There is no need to "free" the buffer at the end (which incidentally you did incorrectly) as vector will do that automatically.
You now pass &buffer[0] to the function, it will point to a null-terminated string and will be writable if the API requires a char* rather than a const char*. (libcurl though takes ... as the 3rd parameter to curl_easy_setopt so you can pass in "anything". You therefore do have to be careful as you won't get warned by the compiler if you accidentally pass in a std::string or std::vector).
Related
I have a requirement where I have to read the error response from backend server which returns 500 Internal Server error. The error response is in JSON Format.
Below is the code snippet used in our application
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
char errbuf[CURL_ERROR_SIZE];
curl = curl_easy_init();
get_request req;
req.buffer =0;
req.len =0;
req.buflen =0;
if(curl)
{
//add url, headers, and parameters to the request
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_URL, endPointUrl);
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
headers = curl_slist_append(headers, m_httpHeadAccept);
headers = curl_slist_append(headers, m_httpContentType);
//callback function used to save response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_String);
req.buffer = (unsigned char*) malloc(CHUNK_SIZE);
req.buflen = CHUNK_SIZE;
req.len = 0;
curl_easy_setopt(curl,CURLOPT_WRITEDATA, (void *)&req);
if (!cookie.IsEmpty())
{
headers = curl_slist_append(headers, m_DBAuthCertficate); //What is difference between this and line no 118?
CString pCookie = "DBAuthTicket=" + cookie;
curl_easy_setopt(curl,CURLOPT_COOKIE, pCookie);
}
else
{
headers = curl_slist_append(headers, m_OAuthToken);
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlparam);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
errbuf[0] = 0;
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 512000);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
{
/* if errors have occured, tell us wath's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
return res;
}
m_response = (char*)req.buffer;
m_errDescription = errbuf;
len = req.len;
buflen = req.buflen;
curl_easy_cleanup(curl);
free(req.buffer);
}
return res;
}
/****************************************************************************
Function: CurlWrite_CallbackFunc_String
Description: Read data from the connected URL
Return: String of data and size
****************************************************************************/
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size*nmemb;
get_request* req = (get_request*)userdata;
while(req->buflen < req->len + rLen + 1)
{
req->buffer = (unsigned char*)realloc(req->buffer,req->buflen + CHUNK_SIZE);
req->buflen += CHUNK_SIZE;
}
memcpy(&req->buffer[req->len], contents, rLen);
req->len += rLen;
req->buffer[req->len] = 0;
return rLen;
}
The above code works fine for the Success 200 OK Requests. It reads the JSON Response just fine. However, when I get a 500 Internal Server error, it does not read the JSON Error response that comes along with it. How do I read the JSON response in this scenario?
By setting the CURLOPT_FAILONERROR option to TRUE, you are telling curl_easy_perform() to fail immediately with CURLE_HTTP_RETURNED_ERROR on any HTTP response >= 400. It will not call the CURLOPT_WRITEFUNCTION callback, as it will simply close the connection and not even attempt to read the rest of the response.
To get the response data you want, simply remove the CURLOPT_FAILONERROR option. Curl's default behavior is to deliver the response data to you regardless of the HTTP response code. In which case, curl_easy_perform() will return CURLE_OK, and you can then retrieve the response code using curl_easy_getinfo(CURLINFO_RESPONSE_CODE) to check if the HTTP request was successful or not.
On a side note, since the code shown is written in C++, I would strongly advise you NOT to use a dynamic char[] buffer for get_request::buffer. Not only because you are not handling malloc()/realloc() failures at all, but also because manual memory management should be avoided in C++ in general. Use std::string or std::vector<char> instead, in which case you can eliminate get_request in this code altogether, eg:
INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
...
std::string resp;
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
...
//curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
/* if errors have occured, tell us what's wrong with 'result'*/
m_response.Format("%s", curl_easy_strerror(res));
curl_easy_cleanup(curl);
return res;
}
m_response = resp.c_str();
m_errDescription = errbuf;
len = resp.size();
buflen = resp.capacity();
curl_easy_cleanup(curl);
return res;
}
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
size_t rLen = size * nmemb;
static_cast<std::string*>(userdata)->append(static_cast<char*>(contents), rLen);
return rLen;
}
I am using libcurl to fetch json data using GET request from a webserver.
This is my sample code:
char *DownloadedResponse;
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in)
{
if (buffer_in != NULL)
{
buffer_in = new char[size*nmemb];
strcpy(buffer_in,data);
DownloadedResponse = buffer_in;
return size * nmemb;
}
return 0;
}
char * DownloadJSON(string URL)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers=NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append( headers, "Content-Type: application/json");
curl_slist_append( headers, "charsets: utf-8");
curl = curl_easy_init();
if (curl)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPGET,1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,writer);
res = curl_easy_perform(curl);
if (CURLE_OK == res)
{
char *ct;
res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct);
if((CURLE_OK == res) && ct)
{
cout<<"\nresponse received: "<<DownloadedResponse;
}
else
{
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
return NULL;
}
}
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
curl = NULL;
}
Here I am able to get json data in DownloadedResponse in callback "writer" of CURLOPT_WRITEFUNCTION.
But if I print using custom pointer of CURLOPT_WRITEDATA,
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
cout<<dataPointer;
Output of dataPointer is empty.
What is the issue here since i able to print json data in callback of CURLOPT_WRITEFUNCTION but not in the pointer of CURLOPT_WRITEDATA
You write a function that takes data read from the network, and writes it to where you want it.
static int writer(char *data, size_t size, size_t nmemb, char *buffer_in){
if (buffer_in != NULL) {
// very bad code which is never executed
}
return 0;
}
In order for that function to write the data, it has to know where to write it, so you tell it to write to NULL
char *dataPointer = NULL;
CURLcode curl_easy_setopt(curl, CURLOPT_WRITEDATA, dataPointer);
What value do you tell it to use as buffer_in? You pass it dataPointer, which is NULL, so you just told it buffer_in = NULL. I think instead you meant to say "the address of dataPointer", which would be &dataPointer.
Technically, I have answered your question now. You passed it NULL for the buffer, so the write function exited immediately. But there's more. Now you get to execute that really bad code in writer().
if (buffer_in != NULL)
{
// if buffer_in already has allocated memory then leak it immediately
// create a new buffer of memory to leak later
buffer_in = new char[size*nmemb];
// store the data in buffer_in
// assume it is null terminated (it is not)
// rather than using the length we already know
strcpy(buffer_in,data);
// remember buffer_in? We don't use it so assign that data pointer to a global variable.
DownloadedResponse = buffer_in;
// return size of this particular chunk of data
return size * nmemb;
}
This function MUST use the length of the data, and not assume data is null terminated (see https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html).
This function MUST be able to handle the data in multiple small pieces by adding them to what it has already read. You can't call new and then discard the new memory. And you can't do that anyway because you just leaked that memory -- every new must be matched with exactly one delete. In fact, you would be very well advised not to use new or delete at all, now that we have the standard library.
This function should use the buffer_in argument you give it rather than a global variable, but you can use a global variable if you want, it's just error prone. It's not literally an error like the other stuff.
The whole point of buffer_in is to give you a persistent data structure where you can accumulate the answers. It probably should be in local scope around the curl_easy_execute, so you can then just return the content from that data structure if you got CURLE_OK. I strongly recommend you write the data to std::vector, so you don't have to keep track of memory allocation. You have trouble with it, but you don't need to do it at all. Modern style says everybody has trouble with it, so just let the standard library handle it.
You claim to follow the example in the docs, which links to https://curl.haxx.se/libcurl/c/getinmemory.html If you look again, you will see what they are doing, and how your code doesn't match. In particular, they pass &chunk (the address of chunk) and then write data into chunk so they keep what was there before.
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
// here is where they get access to the buffer
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
In the call to curl, you will find the struct locally defined, then the remote call:
struct MemoryStruct chunk;
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
res = curl_easy_perform(curl_handle);
if (stuff)
printf("%lu bytes retrieved\n", (long)chunk.size);
Developing on Win64 with Visual Studio 2013 Community, deploying to both Win64 and Linux with cross platform wxWidgets. I am trying to emulate the following curl.exe command line with C++ using libcurl:
curl.exe -X POST -g "single-url-string"
This is for an IoT feature of an app, where an end-user supplies the single-url-string to control their device. The reason this logic is not just executing curl.exe as an external process is because this logic runs in its own thread, and wxWidgets does not support launching external executables when outside the main thread.
Normally when performing a POST with curl.exe, the post data is supplied as an option. This tells curl.exe the operation is a POST to the supplied url, and here is the data for that POST. As you can see, what I'm trying to do is a GET style url (with the parameters embedded in the url) but then changing the operation to a POST. It's done this way because research shows asking end-users to supply two separate url and data strings is simply too complex for them. So we came up with this easier single string end-users must supply, which is usually just copying a string from their device manual without having to interpret the string, much less break it into separate meaningful strings.
So, the issue at hand is: I have my simple C++ libcurl POST routine in two versions, but in both versions the parameters received by the write callback are bad. The two versions are a POST with a single url string, and a POST with the post data provided as a separate option to the url string.
The problems are 1) using the single string version does not execute a POST, and it's write callback params are bad; and 2) using the two string version does execute a POST, but the write callback params are bad, in a different way.
The data pointer parameter in the write callback points to memory address 1 in both versions, the size parameter appears good in both versions, but the nmemb parameter is either a huge random value (single string version) or zero (two string POST version).
Here's my code, and yes I call curl_global_init() at app start.
size_t CX_IOT_THREAD::curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
// storage for transferred data:
const int dataStoreSize = CURL_MAX_WRITE_SIZE + 1;
char dataStore[dataStoreSize];
memset(dataStore, 0, dataStoreSize); // zeroed out
size_t dataSize = size * nmemb; // bytes sent
if (dataSize)
{
memcpy(dataStore, ptr, dataSize); // copy into buffer sized so we'll have a terminating NULL char
wxString msg = wxString::Format(wxT("%s"), dataStore); // send as event, eventually to the log
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg);
// must return byte count processed for libcurl to be happy:
return dataSize; /**/
}
return size; // should be dataSize, but because nmemb is bad, I’m using size; it works.
}
cx_int CX_IOT_THREAD::Post(std::string& url)
{
if (url.length() == 0)
return -1;
char errBuf[CURL_ERROR_SIZE];
errBuf[0] = '\0';
static const char *postthis = "name=Bloke&age=67";
CURLcode ret;
CURL *hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_URL, url.c_str());
curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, postthis);
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis));
curl_easy_setopt(hnd, CURLOPT_ERRORBUFFER, errBuf);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, &CX_IOT_THREAD::curl_write_callback);
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.49.1");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
// curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
ret = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (ret != CURLE_OK)
{
wxString msg = wxString::Format(wxT("Attempted POST failed, libcurl return code '%d'."), (cx_int)ret);
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
cx_int len = strlen(errBuf);
if (len > 0)
msg = wxString::Format("%s%s", errBuf, ((errBuf[len - 1] != '\n') ? "\n" : ""));
else msg = wxString::Format("%s\n", curl_easy_strerror(ret));
mp_queue->Report(CX_IOTTHR_CMD_ACCESS_JOB, msg, (cx_int)ret);
}
return (cx_int)ret;
}
Any ideas why the write callback parameters are bad? Any idea why the single string version does not even do a post? (The single string version is the above with the 2 POSTFIELDS options commented out and the CUSTOMREQUEST one enabled.)
As Igor Tandetnik points out, the callback must be static.
The below code is to get response from a server using wsdl, here the problem is curl returns response but am unable to print it.
Error:
Failed writing body
Failed writing data
#include<stdio.h>
#include<string.h>
#include"../include/curl.h"
size_t write_data(void *ptr, size_t size, size_t count, void *stream)
{
/* ptr - your string variable.
stream - data chuck you received */
printf("%.*s", size, (char*)stream);
}
int main()
{
int res=0,i=0;
char buffer[4098]="",buff[128]="",buf[256]="",buf7[30]="",buf6[30]="",buf5[30]="";
char machineid[]="SUBANI";
char filename1[50]="";
int refno=0,paymode=0,taxtype=0;
FILE *fbc;
memset(filename1,0,sizeof(filename1));
sprintf(filename1,"/mnt/jffs2/Response_Details1.xml");
lk_dispclr();
lk_disptext(1,0,(unsigned char *)"Sending Request",0);
lk_disptext(2,0,(unsigned char *)"Please Wait",0);
memset(buffer,0,sizeof(buffer));
sprintf(buffer,"<?xml version=\"1.0\" encoding=\"utf-8\"?>\
<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:log=\"http://wsdlclassess.application.sims.test.com\">\
<soap:Header>\
</soap:Header>\
<soap:Body>\
<log:loginMethod>\
<log:loginid>%s</log:loginid>\
<log:password>%s</log:password>\
</log:loginMethod>\
</soap:Body>\
</soap:Envelope>","raja","test");
res=GET_FILE1(buffer,filename1);
return 0;
}
int GET_FILE1(char *buffer,char *filename)
{
CURL *curl;
CURLcode res;
struct curl_slist *headers = NULL;
FILE *out_fd = (FILE *) 0;
char errorbuf[300] = "",tmpbuff[128]="";
char errmsg[256];
int Timeout=120; //Default timeout is = 2 mins
int buffer_size = 0;
char urlbuff[256]="";
char mstr[10240];
memset(urlbuff,0,sizeof(urlbuff));
memset(tmpbuff,0,sizeof(tmpbuff));
buffer_size = strlen(buffer);
strcpy(tmpbuff,"http://10.10.1.111:8081/test_server/services/application?wsdl");
tmpbuff[strlen(tmpbuff)]='\0';
curl = curl_easy_init();
if(curl)
{
out_fd = fopen (filename, "w");
curl_easy_setopt(curl, CURLOPT_FILE, out_fd);
printf("%s:Sign-In Request\n", __func__);
headers = curl_slist_append(headers, "Content-type:application/soap+xml; charset=utf-8; action=\"http://wsdlclassess.application.sims.test.com/loginMethod\"");
curl_easy_setopt(curl, CURLOPT_URL, tmpbuff);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, mstr);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, buffer_size);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buffer);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, Timeout);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,errmsg);
printf("The Server%s:Performing Transaction.....\n",__func__);
res = curl_easy_perform(curl);
printf("res=after culreasey perform%d\n",res);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
printf("\nerrorbuf:%s\n",errmsg);
fclose(out_fd);
if(CURLE_OK != res)
{
puts("error occured is\n" );
//ppp_close();
return -1;
}
}
return 0;
}
The error is that you don't return the correct value from the function, in fact you don't return anything.
Also, the data provided to the function is actually the first ptr argument.
I agree that the documentation is not very clear, but it says:
The size of the data pointed to by ptr is size multiplied with nmemb, it will not be zero terminated.
The above line (emphasis mine) tells you that the data is in ptr which is the first argument in the function declaration provided in the documentation.
The documentation also states:
Return the number of bytes actually taken care of. If that amount differs from the amount passed to your function, it'll signal an error to the library. This will abort the transfer and return CURLE_WRITE_ERROR.
You don't return a value from the function, and so you have undefined behavior with a seemingly random value being returned causing the whole operation to fail. To fix this you should return size * count.
You also uses size to print the string, which is the size of the underlying type used (probably 1), your count variable is the number of characters read by CURL. To be fully working, without invoking more undefined behavior (since the data is not terminated) you should call printf like:
printf("%*.*s", size * count, size * count, ptr);
I am trying to read the content of a PHP / HTML file on a remote web server using C++, but haven't found a way to do it. I want to pass GET statements to it, so http://example.com/login.php?user=abc&password=def.
How would I do it?
Your best bet is to use an external library. libcurl is popular and fairly easy to use.
Here's a simple example, you need to add error checking though:
string data;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url_.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWrite);
curl_easy_perform(curl);
Your callback would look something like this:
size_t curlWrite(void *ptr, size_t size, size_t nmemb, void *usrPtr)
{
size_t bytes = size * nmemb;
string *data = static_cast<string *>(usrPtr);
data->append(static_cast<const char *>(ptr), bytes);
return bytes;
}
You can add your GET parameters on the end of the URL.