I am trying to send a POST type message with HTTPS communication using the curl.h library but I have some doubts:
It is necessary to have the certificate in a file (possibly inside the project folder) or it can be taken dynamically (as PostMan does for example).
How should the format of the sending of data be.
How to receive the data is equal to an HTTP?
This is my program:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <curl/curl.h>
BYTE str[20];
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 post_function()
{
CURL *post;
CURLcode ret;
struct string s;
init_string(&s);
curl_global_init(CURL_GLOBAL_DEFAULT);
post = curl_easy_init();
curl_easy_setopt(post, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(post, CURLOPT_URL, "https://example.com/post.php");
curl_easy_setopt(post, CURLOPT_WRITEFUNCTION, writefunc);
curl_easy_setopt(post, CURLOPT_WRITEDATA, &s);
curl_easy_setopt(post, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(post, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(post, CURLOPT_POSTFIELDS, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\nName\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
ret = curl_easy_perform(post);
if(ret == CURLE_OK)
{
sprintf(str, "Msj: %s\n", s.ptr);
}
else
{
//ERROR
}
curl_easy_cleanup(post);
curl_global_cleanup();
}
int main(int argc, char const *argv[])
{
post_function();
return 0;
}
Related
I'm trying to get the content length info and the http status code in the CURLOPT_WRITEFUNCTION callback function. I'm using curl_getinfo on the curl handle to query those fields but curl_getinfo always seems to be returning the default values.
Here is the code
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
struct url_data {
size_t size;
char* data;
CURL *curl;
};
size_t _response_header_callback(void *received_data, size_t size, size_t count, void *response_header) {
//printf("%s ", (char *)received_data);
}
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
size_t index = data->size;
size_t n = (size * nmemb);
char* tmp;
long responseCode = 0;
curl_easy_setopt(data->curl, CURLINFO_RESPONSE_CODE, &responseCode);
curl_off_t cl;
curl_easy_getinfo(data->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl);
printf("\n whats the response Code %d %d", responseCode, cl);
data->size += (size * nmemb);
#ifdef DEBUG
fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
#endif
tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */
if(tmp) {
data->data = tmp;
} else {
if(data->data) {
free(data->data);
}
fprintf(stderr, "Failed to allocate memory.\n");
return 0;
}
memcpy((data->data + index), ptr, n);
data->data[data->size] = '\0';
return size * nmemb;
}
char *handle_url(char* url) {
CURL *curl;
struct url_data data;
data.size = 0;
data.data = malloc(4096); /* reasonable size initial buffer */
if(NULL == data.data) {
fprintf(stderr, "Failed to allocate memory.\n");
return NULL;
}
data.data[0] = '\0';
CURLcode res;
curl = curl_easy_init();
if (curl) {
data.curl = curl;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_RANGE, "0-50");
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_setopt(curl, CURLOPT_RANGE, "51-100");
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
// need to detect that this range isn't satisfiable
curl_easy_setopt(curl, CURLOPT_RANGE, "1551-1600");
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
return data.data;
}
int main(int argc, char* argv[]) {
char* data;
if(argc < 2) {
fprintf(stderr, "Must provide URL to fetch.\n");
return 1;
}
data = handle_url(argv[1]);
if(data) {
printf("%s\n", data);
free(data);
}
return 0;
}
And this is the output
./fetch http://<url>/test/test_key
curl_easy_perform() failed: Couldn't resolve host name
the response Code 0 content length 0
the response Code 0 content length 0
the response Code 0 content length 0
<< download text >
I'm on centos 7 and the libcurl version 7.29.0
Appreciate if someone can let me know what I may be doing wrong. Thank you .
Question 1: When url is downloaded using libcurl, how to preserve the original name of downloaded file ? LibCurl asks programmer to generate filename. Which is maybe easy when URL has the
e.g. in below url its easy to figure out target name is vimqrc.pdf.
http://tnerual.eriogerg.free.fr/vimqrc.pdf)
but when URL is dynamically generating target name e.g.below URL downloads AdbeRdr1010_eu_ES.exe. with wget (no arguments except URL) and curl (argument -O)
http://get.adobe.com/reader/download/?installer=Reader_10.1_Basque_for_Windows&standalone=1%22
How does curl (-O) or wget figures out name of
//invoked as ./a.out <URL>
#include <stdio.h>
#include <curl/curl.h>
char *location = "/tmp/test/out";
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(int argc, char *argv[])
{
CURL *curl;
CURLcode res;
int ret = -1;
if (argc!= 2) {
//invoked as ./a.out <URL>
return -1;
}
curl = curl_easy_init();
if (!curl) {
goto bail;
}
FILE *fp = fopen(location, "wb");
curl_easy_setopt(curl, CURLOPT_URL, argv[1]); //invoked as ./a.out <URL>
/* example.com is redirected, so we tell libcurl to follow redirection */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
/* 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);
ret = 0;
fclose(fp);
bail:
return ret;
}
I found the answer in libcurl source code. Looks like "remote name" is part of the "content-disposition" tag from the header. Libcurl is parsing header and looking for "filename=" in the content-disposition tag. This parsing is done in callback provided through CURLOPT_HEADERFUNCTION option. Finally, in a callback for writing data (provided through CURLOPT_WRITEFUNCTION) this remote name is used to create output file.
If file name is missing, its simply figuring it out from URL itself. This is pretty much code copied from lib curl and little modifications of my own to make it simpler and match my requirement.
#define _GNU_SOURCE
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
typedef unsigned long uint64_t;
typedef struct {
char dnld_remote_fname[4096];
char dnld_url[4096];
FILE *dnld_stream;
FILE *dbg_stream;
uint64_t dnld_file_sz;
} dnld_params_t;
static int get_oname_from_cd(char const*const cd, char *oname)
{
char const*const cdtag = "Content-disposition:";
char const*const key = "filename=";
int ret = 0;
char *val = NULL;
/* Example Content-Disposition: filename=name1367; charset=funny; option=strange */
/* If filename is present */
val = strcasestr(cd, key);
if (!val) {
printf("No key-value for \"%s\" in \"%s\"", key, cdtag);
goto bail;
}
/* Move to value */
val += strlen(key);
/* Copy value as oname */
while (*val != '\0' && *val != ';') {
//printf (".... %c\n", *val);
*oname++ = *val++;
}
*oname = '\0';
bail:
return ret;
}
static int get_oname_from_url(char const* url, char *oname)
{
int ret = 0;
char const *u = url;
/* Remove "http(s)://" */
u = strstr(u, "://");
if (u) {
u += strlen("://");
}
u = strrchr(u, '/');
/* Remove last '/' */
u++;
/* Copy value as oname */
while (*u != '\0') {
//printf (".... %c\n", *u);
*oname++ = *u++;
}
*oname = '\0';
return ret;
}
size_t dnld_header_parse(void *hdr, size_t size, size_t nmemb, void *userdata)
{
const size_t cb = size * nmemb;
const char *hdr_str= hdr;
dnld_params_t *dnld_params = (dnld_params_t*)userdata;
char const*const cdtag = "Content-disposition:";
/* Example:
* ...
* Content-Type: text/html
* Content-Disposition: filename=name1367; charset=funny; option=strange
*/
if (strstr(hdr_str, "Content-disposition:")) {
printf ("has c-d: %s\n", hdr_str);
}
if (!strncasecmp(hdr_str, cdtag, strlen(cdtag))) {
printf ("Found c-d: %s\n", hdr_str);
int ret = get_oname_from_cd(hdr_str+strlen(cdtag), dnld_params->dnld_remote_fname);
if (ret) {
printf("ERR: bad remote name");
}
}
return cb;
}
FILE* get_dnld_stream(char const*const fname)
{
char const*const pre = "/tmp/";
char out[4096];
snprintf(out, sizeof(out), "%s/%s", pre, fname);
FILE *fp = fopen(out, "wb");
if (!fp) {
printf ("Could not create file %s\n", out);
}
return fp;
}
size_t write_cb(void *buffer, size_t sz, size_t nmemb, void *userdata)
{
int ret = 0;
dnld_params_t *dnld_params = (dnld_params_t*)userdata;
if (!dnld_params->dnld_remote_fname[0]) {
ret = get_oname_from_url(dnld_params->dnld_url, dnld_params->dnld_remote_fname);
}
if (!dnld_params->dnld_stream) {
dnld_params->dnld_stream = get_dnld_stream(dnld_params->dnld_remote_fname);
}
ret = fwrite(buffer, sz, nmemb, dnld_params->dnld_stream);
if (ret == (sz*nmemb)) {
dnld_params->dnld_file_sz += ret;
}
return ret;
}
int download_url(char const*const url)
{
CURL *curl;
int ret = -1;
CURLcode cerr = CURLE_OK;
dnld_params_t dnld_params;
memset(&dnld_params, 0, sizeof(dnld_params));
strncpy(dnld_params.dnld_url, url, strlen(url));
curl = curl_easy_init();
if (!curl) {
goto bail;
}
cerr = curl_easy_setopt(curl, CURLOPT_URL, url);
if (cerr) { printf ("%s: failed with err %d\n", "URL", cerr); goto bail;}
cerr = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, dnld_header_parse);
if (cerr) { printf ("%s: failed with err %d\n", "HEADER", cerr); goto bail;}
cerr = curl_easy_setopt(curl, CURLOPT_HEADERDATA, &dnld_params);
if (cerr) { printf ("%s: failed with err %d\n", "HEADER DATA", cerr); goto bail;}
cerr = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
if (cerr) { printf ("%s: failed with err %d\n", "WR CB", cerr); goto bail;}
cerr = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dnld_params);
if (cerr) { printf ("%s: failed with err %d\n", "WR Data", cerr); goto bail;}
cerr = curl_easy_perform(curl);
if(cerr != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(cerr));
}
printf ("Remote name: %s\n", dnld_params.dnld_remote_fname);
fclose(dnld_params.dnld_stream);
/* always cleanup */
curl_easy_cleanup(curl);
ret = 0;
printf ("file size : %lu\n", dnld_params.dnld_file_sz);
bail:
return ret;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf ("Bad args\n");
return -1;
}
return download_url(argv[1]);
}
It's your program, not libcurl that is determining the filename. In your example, you could simple change char *location = "/tmp/test/out"; to char *location = "/tmp/test/vimqrc.pdf"; to get your desired effect.
If you want to derive the download file path programatically given a url and parent directory, you could do something like he following :
int url_to_location(char* location, unsigned int location_length, const char* url, const char* parent_directory)
{
//char location[MAX_PATH];
//const char *url = "http://tnerual.eriogerg.free.fr/vimqrc.pdf";
//const char *parent_directory = "/tmp/test/";
int last_slash_index = -1;
int current_index = (int)strlen(url);
while (current_index >= 0)
{
if (url[current_index] == '/')
{
last_slash_index = current_index;
break;
}
current_index--;
}
unsigned int parent_directory_length = strlen(parent_directory)
if (parent_directory_length <= location_length)
return -1;
strcpy(location, parent_directory);
if (last_slash_index == -1) //no slashes found, use relative url as filename
{
if (parent_directory_length + strlen(url) <= location_length)
return -1;
strcat(location, url);
}
else //use the characters of the url following the last slash as filename
{
if (parent_directory_length + strlen(url + last_slash_index + 1) <= location_length)
return -1;
strcat(location, url + last_slash_index + 1);
}
return strlen(location);
}
I'm trying to get header contents of the webpage using libcurl and trying to store it in an charecter buffer .The program gives segmentation faults . I'm beginner to libcurl and i 'm not sure where the problem exists .Here's the code below .
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <curl/curl.h>
struct buf {
char *buffer;
size_t bufferlen;
size_t writepos;
} buffer = {0};
struct buf HeaderBuffer;
static size_t write_data(void *ptr, size_t size, size_t nmemb, char *string)
{
size_t nbytes = size*nmemb;
if (!HeaderBuffer.buffer) {
HeaderBuffer.buffer = malloc(1024);
HeaderBuffer.bufferlen = 1024;
HeaderBuffer.writepos = 0;
}
if (HeaderBuffer.writepos + nbytes < HeaderBuffer.bufferlen) {
HeaderBuffer.bufferlen = 2 * HeaderBuffer.bufferlen;
HeaderBuffer.buffer = (char *)realloc(HeaderBuffer.buffer, buffer.bufferlen);
}
assert(HeaderBuffer.buffer != NULL);
memcpy(HeaderBuffer.buffer+HeaderBuffer.writepos,ptr,nbytes);
return nbytes;
}
int main(void)
{
CURL *curl_handle;
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://example.com");
/* send all data to this function */
curl_easy_setopt(curl_handle,CURLOPT_HEADERFUNCTION, write_data);
/* get it! */
curl_easy_perform(curl_handle);
printf("%s",HeaderBuffer.buffer);
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
return 0;
}
EDIT :
#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 = (char *)malloc(s->len+1);
if (s->ptr == NULL) {
fprintf(stderr, "malloc() failed\n");
exit(1);
}
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 =(char *) 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);
//sprintf (s->ptr+s->len,"New header:\n%s\n",(char *)ptr);
s->ptr[new_len] = '\0';
s->len = new_len;
return size*nmemb;
}
int main(void)
{
CURL *curl;
CURLcode res;
char url[50];
char *ptr=NULL;
int i=0;
memset(Buffer,0,255);
strcpy(url,"http://");
strncat(url,"74.125.236.176",strlen("74.125.236.176"));
curl = curl_easy_init();
if(curl) {
struct string s;
init_string(&s);
curl_easy_setopt(curl, CURLOPT_URL,url);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, writefunc);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &s);
res = curl_easy_perform(curl);
printf("\n \n %s",Buffer);
printf("%s\n", s.ptr);
free(s.ptr);
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
Here I think you should do,
HeaderBuffer.bufferlen = nbytes * HeaderBuffer.bufferlen;
since you do not know nbytes,it may be greater than 2*HeaderBuffer.bufferlen.
I have homework where I need somehow to compare two HTTP responses. I am writing it on C and I use libcurl to make things easier. I am calling the function that uses libcurl to do a HTTP request and response from another function, and I want to return the HTTP response as a char *. Here is my code so far (it crashes):
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
size_t written;
written = fwrite(ptr, size, nmemb, stream);
return written;
}
char *handle_url(void) {
CURL *curl;
char *fp;
CURLcode res;
char *url = "http://www.yahoo.com";
curl = curl_easy_init();
if (curl) {
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);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
//printf("\n%s", fp);
}
return fp;
}
This solution C libcurl get output into a string works, but not in my case because I just want to return the string to the calling function.
Any ideas?
Fixed it for you. You need to handle the case where the write_data() function is called multiple times, and pass it the right kind of parameter. You also need to keep track of how big a structure you've got, so you can allocate enough memory.
I left in a debug printf in the write_data function to help you understand how it works.
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
struct url_data {
size_t size;
char* data;
};
size_t write_data(void *ptr, size_t size, size_t nmemb, struct url_data *data) {
size_t index = data->size;
size_t n = (size * nmemb);
char* tmp;
data->size += (size * nmemb);
#ifdef DEBUG
fprintf(stderr, "data at %p size=%ld nmemb=%ld\n", ptr, size, nmemb);
#endif
tmp = realloc(data->data, data->size + 1); /* +1 for '\0' */
if(tmp) {
data->data = tmp;
} else {
if(data->data) {
free(data->data);
}
fprintf(stderr, "Failed to allocate memory.\n");
return 0;
}
memcpy((data->data + index), ptr, n);
data->data[data->size] = '\0';
return size * nmemb;
}
char *handle_url(char* url) {
CURL *curl;
struct url_data data;
data.size = 0;
data.data = malloc(4096); /* reasonable size initial buffer */
if(NULL == data.data) {
fprintf(stderr, "Failed to allocate memory.\n");
return NULL;
}
data.data[0] = '\0';
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
}
return data.data;
}
int main(int argc, char* argv[]) {
char* data;
if(argc < 2) {
fprintf(stderr, "Must provide URL to fetch.\n");
return 1;
}
data = handle_url(argv[1]);
if(data) {
printf("%s\n", data);
free(data);
}
return 0;
}
Note: compile with gcc -o test test.c -lcurl (assuming you pasted into test.c). Use gcc -o test test.c -lcurl -DDEBUG to see the test printf() calls.
Disclaimer: this is ugly, quick-and-dirty code. There may be bugs. Please see the more robust, better commented example here.
I got the code from C libcurl get output into a string. I modified it and I want to use it to access the Twitter Stream. I added curl_easy_setopt(curl, CURLOPT_URL, "http://stream.twitter.com/1/statuses/sample.json"); and curl_easy_setopt(curl, CURLOPT_USERPWD, "neilmarion:my_password"); in the code. But the problem is whenever I execute it, there is no output. What must be the problem? Thanks.
#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, "http://stream.twitter.com/1/statuses/sample.json");
curl_easy_setopt(curl, CURLOPT_USERPWD, "neilmarion:my_password");
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 quickest next step is probably to set CURLOPT_HEADER, to include headers in the body output. Most likely, I would guess it is failing on security, and you'll see the details in the headers.