How to download a ZIP file from server using cURL in C? - c

I am developing on Linux platform. I am using libcurl and able to receive json response and saving it to file. Below is the code.
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <string.h>
#define URL "http://www.joes-hardware.com/tools.html"
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
//const char url[] = "http://www.joes-hardware.com/tools.html";
char *url= URL;
char outfilename[FILENAME_MAX] = "./json";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Now I need to fetch a zip file from the server. Suppose the URL is of the format shown below:
#define URL "https://Server/File.zip"
For such URL, the code is not able to save the zip file.
How to achieve this?

I resolved the issue. The problem was with HTTPS connection. I had to add certificates for HTTPS.
Based on below links:
Can't connect to HTTPS site using cURL. Returns 0 length content instead
Getting no content from a HTTPS connection using CURL
#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <string.h>
#include <stdlib.h>
#define false 0
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
const char url[] = "https://example.com/filename.zip";
const char outfilename[FILENAME_MAX] = "./json.zip";
curl_version_info_data * vinfo = curl_version_info(CURLVERSION_NOW);
if(vinfo->features & CURL_VERSION_SSL){
printf("CURL: SSL enabled\n");
}else{
printf("CURL: SSL not enabled\n");
}
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
/* Setup the https:// verification options. Note we */
/* do this on all requests as there may be a redirect */
/* from http to https and we still want to verify */
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CAINFO, "./ca-bundle.crt");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
int i=fclose(fp);
if( i==0)
system("unzip -j json.zip");
}
return 0;
}

Related

Not completed downloading of file (segmentation fault) while using curl on Ubuntu 14.04

After compiling my program i get this error:
I'm using Code::Blocks.Program is written to be easy download manager. Problem occurs with all types of files (pdf,txt,jpg). Here's my code. I don't know why is it happening. Please help.
#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
int x;
char y[200];
char page;
char* outfilename;
char* path_pdf = "/home/user/Desktop/document.pdf";
char* path_jpg = "/home/user/Desktop/picture.jpg";
char* path_txt = "/home/user/Desktop/document.txt";
char FILEPATH[3] = {path_pdf, path_jpg, path_txt};
printf("Enter file url: \n"); // for example http://oi58.tinypic.com/15nk3de.jpg
scanf ("%s",y);
char *url = y;
printf("Choose type of file:\n [0] - pdf\n [1] - jpg\n [2] - txt\n "); //choose 1
scanf("%d",&x);
outfilename = FILEPATH[x];
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (res == CURLE_OK)
{
printf("File downloaded!\n");
}
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
char FILEPATH[3] = {path_pdf, path_jpg, path_txt};
Is an array of char's (you want an array of strings), change to:
char *FILEPATH[3] = {path_pdf, path_jpg, path_txt};

C program for downloading files with curl

I am trying to write a program in C to download some files.
The source code:
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <curl/easy.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(){
if(curl_global_init(CURL_GLOBAL_ALL)){
printf("curl error. Exiting.\n");
return 1;
}
char links[3][100] = {
"http://download.freeroms.com/nes_roms/08/big_nose_the_caveman.zip",
"http://download.freeroms.com/nes_roms/02/contra.zip",
"http://download.freeroms.com/nes_roms/08/super_mario_bros._(usajapan).zip"};
int n = 0, k = 0;
char *lastslash;
char* name;
CURL *handle = curl_easy_init();
CURLcode res;
FILE *file;
while(n<3){
lastslash = strrchr(links[n], '/');
name = lastslash ? lastslash + 1 : links[n];
printf("\nURL: %s\n", links[n]);
printf("Filename: %s\n", name);
curl_easy_setopt(handle, CURLOPT_URL, links[n]);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data);
file = fopen(name, "wb");
res = curl_easy_perform(handle);
fclose(file);
n++;
}
curl_easy_cleanup(handle);
return 0;
}
I can compile it, but this is the output when I run it :
URL: http://download.freeroms.com/nes_roms/08/big_nose_the_caveman.zip
Filename: big_nose_the_caveman.zip
Segmentation fault (core dumped)
My compiler setting:
gcc dl.c -lcurl -o dl
I found out that the problem occurs when it tries to execute curl_easy_perform(), but I don't know what to do with it.
try this coding.
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/aaa.txt");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
You need to open the file before you set the callback data. The FILE* is stored by value, not reference.
file = fopen(name, "wb");
curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);

the downloaded file is not saving in given location

#define CURL_STATICLIB
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Some one has posted this code for a question. But on execution of this file the downloaded file is not saving in C drive rather a new txt file will be generating with name "C:\cat.txt"... I want the downloaded file will be stored in my desired location in hard drive.. can any one help me...
First, the default CURLOPT_WRITEFUNCTION accepts a FILE * and uses fwrite using whatever's been set with CURLOPT_WRITEDATA, so you don't need to override the function unless you're using curl as a Win32 DLL.
Also, you aren't checking the return from fopen, which may fail.
I suspect in this case that you either aren't recompiling your code, or are running a different binary from the one you built

C libcurl get output into a string

I want to store the result of this curl function in a variable, how can I do so?
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
thanks, I solved it like this:
#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>
function_pt(void *ptr, size_t size, size_t nmemb, void *stream){
printf("%d", atoi(ptr));
}
int main(void)
{
CURL *curl;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_pt);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
system("pause");
return 0;
}
You can set a callback function to receive incoming data chunks using curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, myfunc);
The callback will take a user defined argument that you can set using curl_easy_setopt(curl, CURLOPT_WRITEDATA, p)
Here's a snippet of code that passes a buffer struct string {*ptr; len} to the callback function and grows that buffer on each call using realloc().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct string {
char *ptr;
size_t len;
};
void init_string(struct string *s) {
s->len = 0;
s->ptr = malloc(s->len+1);
if (s->ptr == NULL) {
fprintf(stderr, "malloc() failed\n");
exit(EXIT_FAILURE);
}
s->ptr[0] = '\0';
}
size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
{
size_t new_len = s->len + size*nmemb;
s->ptr = realloc(s->ptr, new_len+1);
if (s->ptr == NULL) {
fprintf(stderr, "realloc() failed\n");
exit(EXIT_FAILURE);
}
memcpy(s->ptr+s->len, ptr, size*nmemb);
s->ptr[new_len] = '\0';
s->len = new_len;
return size*nmemb;
}
int main(void)
{
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
struct string s;
init_string(&s);
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
res = curl_easy_perform(curl);
printf("%s\n", s.ptr);
free(s.ptr);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
The following answer is the C++ way to do it, with std::string, instead of null-terminated string. It still uses a callback function (there's no way around it), but also handles allocation error using try/catch.
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t CurlWrite_CallbackFunc_StdString(void *contents, size_t size, size_t nmemb, std::string *s)
{
size_t newLength = size*nmemb;
try
{
s->append((char*)contents, newLength);
}
catch(std::bad_alloc &e)
{
//handle memory problem
return 0;
}
return newLength;
}
int main()
{
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
std::string s;
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //remove this to disable verbose output
/* Perform the request, res will get the return code */
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);
}
std::cout<<s<<std::endl;
std::cout<< "Program finished!" << std::endl;
}
From reading the manual here: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html I think you need several calls to CURL_SETOPT, the first being the URL you want to process, the second being something like:
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_ptr);
Where function_ptr matches this signature:
size_t function( void *ptr, size_t size, size_t nmemb, void *stream)
What happens here is you denote a callback function which libcurl will call when it has some output to write from whatever transfer you've invoked. You can get it to automatically write to a file, or pass it a pointer to a function which will handle the output itself. Using this function you should be able to assemble the various output strings into one piece and then use them in your program.
I'm not sure what other options you may have to set / what else affects how you want your app to behave, so have a good look through that page.
Here's a C++ flavor of the accepted answer from alex-jasmin
#include <iostream>
#include <string>
#include <curl/curl.h>
size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s)
{
s->append(static_cast<char *>(ptr), size*nmemb);
return size*nmemb;
}
int main(void)
{
CURL *curl = curl_easy_init();
if (curl)
{
std::string s;
curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
CURLcode res = curl_easy_perform(curl);
std::cout << s << std::endl;
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}

Download file using libcurl in C/C++

I am building an application (on windows using Dev-C++) and I want it to download a file. I am doing this using libcurl (I have already installed the source code using packman). I found a working example (http://siddhantahuja.wordpress.com/2009/04/12/how-to-download-a-file-from-a-url-and-save-onto-local-directory-in-c-using-libcurl/) but it doesn't close the file after download is complete. I would like an example of how to download a file in C.
The example you are using is wrong. See the man page for easy_setopt. In the example write_data uses its own FILE, *outfile, and not the fp that was specified in CURLOPT_WRITEDATA. That's why closing fp causes problems - it's not even opened.
This is more or less what it should look like (no libcurl available here to test)
#include <stdio.h>
#include <curl/curl.h>
/* For older cURL versions you will also need
#include <curl/types.h>
#include <curl/easy.h>
*/
#include <string>
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
size_t written = fwrite(ptr, size, nmemb, stream);
return written;
}
int main(void) {
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://localhost/aaa.txt";
char outfilename[FILENAME_MAX] = "C:\\bbb.txt";
curl = curl_easy_init();
if (curl) {
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}
Updated: as suggested by #rsethc types.h and easy.h aren't present in current cURL versions anymore.
Just for those interested you can avoid writing custom function by passing NULL as last parameter (if you do not intend to do extra processing of returned data).
In this case default internal function is used.
Details
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTWRITEDATA
Example
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl;
FILE *fp;
CURLcode res;
char *url = "http://stackoverflow.com";
char outfilename[FILENAME_MAX] = "page.html";
curl = curl_easy_init();
if (curl)
{
fp = fopen(outfilename,"wb");
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
}
return 0;
}

Resources