output in microsoft visual studio - winforms

i'm making an app wich shows the output of a php page using libcurl
in terminal mode this app works great
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "www.sglroostermail.nl/api/getChanges.php?y=1);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
but now i want to make a gui app.
For the GUI i use microsoft visual studio 2015 --> CLR empty project.
but the terminal code gives his own output
and i want to show the output in a textbox or a label.
for example: (this is the part of the MYForm.h
#pragma endregion
private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
}
private: System::Void textBox1_TextChanged_1(System::Object^ sender, System::EventArgs^ e) {
}
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
String ^ klas = textBox1->Text;
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, "www.sglroostermail.nl/api/getChanges.php?y=", klas);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
}
can anyone tell me how to store the output of the first code in a variable, label or textbox?

You need to tell libcurl what to do with the output it receives from the remote server.
There are two options to use here:
CURLOPT_WRITEFUNCTION: This tells libcurl which function to call when it receives data.
CURLOPT_WRITEDATA: This is a void* that will be passed to the write function.
By default, these are set to fwrite and stdout, which is why libcurl writes the output to the terminal. (Note: The options are named 'write' because a common operation is to download the file and write it to disk. Think of it as what to do with the data after you receive it, don't think of it as 'reading' from the server.)
If you want to capture the data, here's what I would do:
Allocate a container of some sort: Have a byte buffer, and set CURLOPT_WRITEDATA to a pointer to that buffer.
Create a method to copy the data to the buffer, and set that as CURLOPT_WRITEFUNCTION.
Because libcurl is an unmanaged library, I'd use std::vector<uint8_t> as the buffer. The write callback must have the prototype size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);, and I think it's easier to deal with the unmanaged vector than it is to pin a managed buffer & deal with that properly.

Related

Curl: How can I clear stored data while the program is running

I made a text status bar program in C that uses curl to connect to a weather site, grab data, get the correct info, and display it in the bar. That part of the program works perfectly. The issue is that after the program successfully connects to the server, downloads the page, and stores it to memory, and after I extract my weather data and display it, the program doesn't release that memory downloaded, so it takes an extra 15Mb of ram to run until I close the program.
This seems to happen right as I issue:
curl_easy_perform(curl);
I don't know how to release this memory without closing the program, and I need the program to remain open. I don't want to have to make a separate program and call it, as I would like to use curl in that program for other features (such as pinging the net to check for a connection) in the future, so I might as well just use it while it's there.
I've had this working in C++ perfectly with NO extra memory usage, but I've yet to find the code for C that properly clears the memory.
In order to get to the heart of the situation, I'm going to use code that focuses solely on the issue at hand and that has the exact same issue. It is taken from this website:
https://curl.haxx.se/libcurl/c/getinmemory.html
As an aside, I've also tried using this code with no luck. I thought maybe if I just dumped the data, I could at least ping a site and check if my net is up, but it STILL holds that 15Mb of memory:
https://curl.haxx.se/libcurl/c/simple.html
I've heard of curl leaks, but I cannot truly verify if this is an actual leak or if I am just not releasing the memory properly.
mycurl.c:
/*
Compile with:
gcc -O2 -Wall mycurl.c -o mycurl -L/usr/include/curl/lib -lcurl
Courtesy of:
https://curl.haxx.se/libcurl/c/getinmemory.html
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<curl/curl.h>
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int main(void)
{
CURL *curl;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1);
chunk.size = 0;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com/");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
res = curl_easy_perform(curl);
/* check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
return 1;
}
printf("%lu bytes retrieved\n", (unsigned long)chunk.size);
curl_easy_cleanup(curl);
free(chunk.memory);
curl_global_cleanup();
/* Everything is all cleaned up, so the memory usage should be back to normal at this point. */
printf("Waiting 3 seconds... Watch to see if the memory levels change between now and when the program says 'Done!' If you see a change in memory, there is likely a leak.)\n");
/* When the program shuts down, the memory is released, but it should release after we cleanup and before the program terminates. Lets give ourselves a few seconds to spot if this is the case. Make sure to have 'top' or 'htop' open and */
sleep(3); // top and htop default at 3 second intervals, so we need more than that to spot a change
printf("\nDone!\nDid the memory change?\n");
return 0;
}
The memory never clears before the program shuts down. I would like to be able to clear that memory and reuse the curl command.
I'm sure you are wondering why am I griping over 15Mb?
The code is to be used for a Raspberry Pi Zero, which is limited to 512Mb ram (cannot be upgraded), and I don't want to give away 15Mb if I really don't have to. Currently, I am running this code on a Raspberry Pi 3B+; though, I've had this issue with curl for a long time, even on other architectures (x86/64).
Would love to get it solved. Thanks. :)
**** EDIT ****
Credit to hlscalon for his bug reference link and (so far as I can see) finding the cause.
apt install libcurl4-openssl-dev
The above code, on Debian, will uninstall 'libcurl4-gnutls-dev' and install libcurl4-openssl-dev in its place. This fixed the situation.
Below is a list of sites before and after I switched to libcurl4-openssl-dev. The change in memory consumption went from 15Mb to 1Mb or less on all sites.
To allow other users to easily test if this is perhaps the case for them, I kept all my testing code, which can be quickly cut & pasted and ran:
//curl_easy_setopt(curl, CURLOPT_URL, "https://kernel.org"); // before +15Mb, after +1Mb
//curl_easy_setopt(curl, CURLOPT_URL, "https://stackoverflow.com"); // before +15Mb, after +1Mb
//curl_easy_setopt(curl, CURLOPT_URL, "https://blog.apastyle.org/"); // before +15Mb, after +1Mb
//curl_easy_setopt(curl, CURLOPT_URL, "http://forums.debian.net"); // before +0Mb, after +1Mb
//curl_easy_setopt(curl, CURLOPT_URL, "http://support.blog.com/create-a-blog/"); // before +0Mb, after +1Mb
//curl_easy_setopt(curl, CURLOPT_URL, "http://blog.alz.org/"); // before +0Mb, after +0Mb
Night and day difference in memory usage. So glad to have this one solved. Thanks again to hlscalon and FredK for their super-hero speed replies and tips.
Consider this post still open to replies pertaining to libcurl4-gnutls-dev being fixed. Sadly, this seems to have been a bug that has been around for a couple of years, so I'm not holding my breath.

a raw libcurl JSON PUT request using C

I am currently writing a REST-like client that only is required to do PUT requests.
Problem:
Running the program is not giving me the correct results on the URL's API and I do not know why.
Using curl_easy_perform(curl) does not throw an error when called. But the expected result is not generated on the URL's API.
Using curl_easy_send(curl,..,..,..) throws a : unsupported protocol error
Assumption:
I am assuming the order in which I am using the curl_easy_opts is a problem? And I am even missing a couple of key lines?
I have been reading on here of how other people do PUT requests and have been using their methods.
Summary of Program:
My program prompts the user for some string/character data, and from that, I construct the strings myself such as the header and the payload. The header and payload are both in JSON format but the payload is simply a string ( in this case, a char *str = (char *)mallo.. etc). How the header is constructed is shown below.
My header is being constructed using
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
//there is more content being appended to the header
The CURL function calls :
//init winsock stuff
curl_global_init(CURL_GLOBAL_ALL);
//get a curl handle
curl = curl_easy_init();
if(curl){
//append the headers
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
//specify the target URL
curl_easy_setopt(curl, CURLOPT_URL, url);
//connect ( //i added this here since curl_easy_send() says it requires it. )
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY,1L);
//specify the request (PUT in our case)
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
//append the payload
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);
res = curl_easy_perform(curl);
//res = curl_easy_send(curl, payload, strlen(payload),&iolen);
//check for errors
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
You should not be using the CURLOPT_CONNECT_ONLY option or curl_easy_send() function, those are intended to be used for custom, non-HTTP protocols.
See this page for an example of how to do a PUT request with libcurl. Basically, you want to enable the CURLOPT_UPLOAD and CURLOPT_PUT options to say that you're doing a PUT request and to enable uploading a body with the request, and then you set the CURLOPT_READDATA and CURLOPT_INFILESIZE_LARGE options to tell libcurl how to read the data you're uploading and how big the data is.
In your case, if you already have the data in memory, then you don't need to read it out of a file, and you can just memcpy() it inside your read callback.
Example code copied below:
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel#haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <curl/curl.h>
/*
* This example shows a HTTP PUT operation. PUTs a file given as a command
* line argument to the URL also given on the command line.
*
* This example also uses its own read callback.
*
* Here's an article on how to setup a PUT handler for Apache:
* http://www.apacheweek.com/features/put
*/
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
{
size_t retcode;
curl_off_t nread;
/* in real-world cases, this would probably get this data differently
as this fread() stuff is exactly what the library already would do
by default internally */
retcode = fread(ptr, size, nmemb, stream);
nread = (curl_off_t)retcode;
fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T
" bytes from file\n", nread);
return retcode;
}
int main(int argc, char **argv)
{
CURL *curl;
CURLcode res;
FILE * hd_src ;
struct stat file_info;
char *file;
char *url;
if(argc < 3)
return 1;
file= argv[1];
url = argv[2];
/* get the file size of the local file */
stat(file, &file_info);
/* get a FILE * of the same file, could also be made with
fdopen() from the previous descriptor, but hey this is just
an example! */
hd_src = fopen(file, "rb");
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if(curl) {
/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
/* enable uploading */
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* HTTP PUT please */
curl_easy_setopt(curl, CURLOPT_PUT, 1L);
/* specify target URL, and note that this URL should include a file
name, not only a directory */
curl_easy_setopt(curl, CURLOPT_URL, url);
/* now specify which file to upload */
curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
/* provide the size of the upload, we specicially typecast the value
to curl_off_t since we must be sure to use the correct data size */
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
(curl_off_t)file_info.st_size);
/* Now run off and do what you've been told! */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
fclose(hd_src); /* close the local file */
curl_global_cleanup();
return 0;
}
I agree, don't use CUSTOMREQUEST. One detail that is being missed on every related to PUT and CURL I've seen here is that you NEED to set the file size, otherwise you'll get HTTP error 411.
Use CURLOPT_INFILESIZE or CURLOPT_INFILESIZE_LARGE for that.
See more details here:
How do I send long PUT data in libcurl without using file pointers?
I know this is a very old question, but in case somebody want to use libcurl with GLib and json-glib to send a JSON with PUT request.
Code below works for me:
#include <curl/curl.h>
#include <json-glib/json-glib.h>
//this is callback function for CURLOPT_READFUNCTION:
static size_t
curlPutJson ( void *ptr, size_t size, size_t nmemb, void *_putData )
{
GString *putData = ( GString * ) _putData;
size_t realsize = ( size_t ) putData->len;
memcpy ( ptr, putData->str, realsize );
return realsize;
}
/*now inside main or other function*/
//json_to_string ( jsonNode, FALSE ) is from json-glib to stringify JSON
//created in jsonNode
GString *putData = g_string_new ( json_to_string ( mainNode, FALSE ) );
//now goes curl as usual: headers, url, other options and so on
//and 4 most important lines
curl_easy_setopt ( curl, CURLOPT_READFUNCTION, curlPutJson );
curl_easy_setopt ( curl, CURLOPT_UPLOAD, 1L );
curl_easy_setopt ( curl, CURLOPT_READDATA, putData ); //GString
curl_easy_setopt ( curl, CURLOPT_INFILESIZE, putData->len ); //type long

libcurl on Windows CE - curl_easy_perform(...) doesn't return

As part of a project, I need to implement libcurl on a Custom WindowsCE device that then talks to a server over wired LAN and GETs, POSTs and DELETEs data ( The programming language I am using is C). This is as far as I've progressed:
I found a couple of tutorials to re-implement the library for my CE device.
( http://stasyan.wordpress.com/2009/12/08/libcurl-for-windows-mobile-and-windows-ce/ , and
http://rxwen.blogspot.com/2012/05/port-libcurl-to-wince.html do a really good job.)
I was able to build a DLL and a Static libcurl library, which I then linked to my Visual Studio 2005 application.
I connected my custom CE device and the server to a switch. I am able to ping the server from the CE device and vice versa, so I know that they are both able to send and receive data to each other.
My server (Linux) uses a lighttpd web-server which works perfectly fine when I request the URL (shown in code, below) from my Windows PC which is also connected to the N/W switch.
This is the code I used for my test app:
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
printf("STARTING APP...\n");
CURL *curl;
CURLcode res;
printf("curl = curl_easy_init();\n");
curl = curl_easy_init();
if(curl)
{
printf("Enabling followlocation...\n");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1l);
printf("Setting Verbose...\n");
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
printf("Setting Connect time out...\n");
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
printf("Setting Server time out...\n");
curl_easy_setopt(curl, CURLOPT_ACCEPTTIMEOUT_MS , 5000);
printf("Setting URL...\n");
curl_easy_setopt(curl, CURLOPT_URL, "http://165.26.79.10:8080");
/* Perform the request, res will get the return code */
printf("Perform(curl)...;\n");
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
else
printf("\nGET sucessful.\n");
/* always cleanup */
curl_easy_cleanup(curl);
}
getchar();
return 0;
}
getchar();
return 0;
}
My problem is that the app never returns. It doesn't even progress past curl_easy_perform(curl).
This is the output I get (Google Drive link): [http://goo.gl/IKgiqW]
What could I possibly be doing wrong??
I managed to find out what the problem was.
The curl_easy_perform(...) command goes into an infinite loop caused due to a bug in one of the files in the compatibility library. It is referenced in a cURL development forum (google it), but I've summarized it below:
The WCE compatibility library I used (https://github.com/mauricek/wcecompat/) has a file called wce211_string.c. A function within the file, _CRTIMP char* __cdecl strrchr(const char* s, int c), uses a const char* p pointer that isn't decremented. To fix this bug, add the line: p-=1; after the line 'return (char*)p;', so the function will read as follows:
_CRTIMP char* __cdecl strrchr(const char* s, int c)
{
const char* p = s + strlen(s) - 1;
while (p >= s)
{
if (*p == c)
return (char*)p;
p -= 1;
}
return NULL;
}
This fixes the infinite looping problem.

How to resume file download through FTP using Curl in C?

I am trying to download file using FTP, and in between if connection terminates then it should resume from where it was stop. My problem is that using following code snippet I able to continue download if I close connection and then again connect it, but if I am do so at server site then I am not able to resume download, and program goes into infinites state.
#include <stdio.h>
#include <curl/curl.h>
/*
* This is an example showing how to get a single file from an FTP server.
* It delays the actual destination file creation until the first write
* callback so that it won't create an empty file in case the remote file
* doesn't exist or something else fails.
*/
struct FtpFile {
const char *filename;
FILE *stream;
};
static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct FtpFile *out=(struct FtpFile *)stream;
if(out && !out->stream) {
/* open file for writing */
out->stream=fopen(out->filename, "wb");
if(!out->stream)
return -1; /* failure, can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
int main(void)
{
CURL *curl;
CURLcode res;
struct FtpFile ftpfile={
"dev.zip", /* name to store the file as if succesful */
NULL
};
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
/*
* You better replace the URL with one that works!
*/
curl_easy_setopt(curl, CURLOPT_URL,
"ftp://root:password#192.168.10.1/dev.zip");
/* Define our callback to get called when there's data to be written */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite);
/* Set a pointer to our struct to pass to the callback */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile);
/* Switch on full protocol/debug output */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
if(CURLE_OK != res) {
/* we failed */
fprintf(stderr, "curl told us %d\n", res);
}
}
if(ftpfile.stream)
fclose(ftpfile.stream); /* close the local file */
curl_global_cleanup();
return 0;
}
Could any one tell me that how can I resume download if connection is closed by remote site.
Any help would be appreciated
Thanks,
Yuvi
Add a varaible to the ftpfile struct to aware your write function of the need to appeand and tell libcurl to resume the download from the end of the destination file by setting CURLOPT_RESUME_FROM to the number of bytes downloaded already:
struct FtpFile
{
const char *pcInfFil;
FILE *pFd;
int iAppend;
};
In main, if you want to resume:
curl_easy_setopt(curl, CURLOPT_RESUME_FROM , numberOfBytesToSkip);
If you the file doesn't already exist, or it is not a resumed download but a new download, be sure to set CURLOPT_RESUME_FROM back to 0.
In my_fwrite:
out->stream=fopen(out->filename, out->iAppend ? "ab":"wb");
P.S. if where you need to resume the file is larger than a long (2GB), look into CURLOPT_RESUME_FROM_LARGE and CURL_OFF_T_C()
In response to comment requesting additional information on how to know when a transfer failed:
After you call curl easy perform call:
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
Retrieve from the curl context:
CURLINFO_HEADER_SIZE
CURLINFO_CONTENT_LENGTH_DOWNLOAD
Add them together and make sure they equal
CURLINFO_SIZE_DOWNLOAD
If not try to reperform the context.
And be sure to use the latest version of curl, it should timeout after 60 seconds of not hearing from the FTP server it is downloading from.
You may use the CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT parameters to specify a connect timeout and maximum execution time on a per-handle basis.
The other way (that only works if you're using the easy interface, not the multi one) is to use a socket option callback, that you can set using CURLOPT_SOCKOPTFUNCTION. In it, you must call setsockopt() for the SO_RCVTIMEO parameter to the maximum amount of time a connection can be idle before it should be dropped. i.e. if no bytes have been received in the last 5 seconds, then drop the connection.

hide curl_easy_perform

How can I hide curl_easy_perform output (in a shell)?
This is in regards to a C application.
Use CURLOPT_NOBODY in curl_easy_setopt().
Example:
...
CURL *curl;
CURLcode statusCode;
curl = curl_easy_init();
if(curl){
curl_easy_setopt(curl, CURLOPT_URL, "http://www.google.com/");
//CURLOPT_NOBODY does the trick
curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
curl_easy_perform(curl);
...
Link to docs: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTNOBODY
Set the CURLOPT_WRITEFUNCTION and/or CURLOPT_WRITEDATA options:
FILE *f = fopen("target.txt", "wb");
curl_easy_setopt(handle, CURLOPT_WRITEDATA, f);
By default, libcurl writes output to stdout. When you override this (which is what almost any application will do), it will write to another file or to pass chunks of output to a callback. See the documentation for CURLOPT_WRITEFUNCTION for more details.
As Joey said, CURLOPT_WRITEFUNCTION will allow you to completely disregard all output. Just set up a callback that does absolutely nothing if you want the data to just go away, without being written to any file descriptor.
For instance,
/* Never writes anything, just returns the size presented */
size_t my_dummy_write(char *ptr, size_t size, size_t nmemb, void *userdata)
{
return size * nmemb;
}
Then in your options:
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &my_dummy_write);
Or, point the file handle at a NULL device (a lot easier).

Resources