229 lines
7.6 KiB
C
229 lines
7.6 KiB
C
|
// EASYCURL :: https://github.com/prideout/par
|
||
|
// Wrapper around libcurl for performing simple synchronous HTTP requests.
|
||
|
//
|
||
|
// Distributed under the MIT License, see bottom of file.
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// BEGIN PUBLIC API
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
#ifndef PAR_EASYCURL_H
|
||
|
#define PAR_EASYCURL_H
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
|
||
|
typedef unsigned char par_byte;
|
||
|
|
||
|
// Call this before calling any other easycurl function. The flags are
|
||
|
// currently unused, so you can just pass 0.
|
||
|
void par_easycurl_init(unsigned int flags);
|
||
|
|
||
|
// Allocates a memory buffer and downloads a data blob into it.
|
||
|
// Returns 1 for success and 0 otherwise. The byte count should be
|
||
|
// pre-allocated. The caller is responsible for freeing the returned data.
|
||
|
// This does not do any caching!
|
||
|
int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes);
|
||
|
|
||
|
// Downloads a file from the given URL and saves it to disk. Returns 1 for
|
||
|
// success and 0 otherwise.
|
||
|
int par_easycurl_to_file(char const* srcurl, char const* dstpath);
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
// END PUBLIC API
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
#ifdef PAR_EASYCURL_IMPLEMENTATION
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <curl/curl.h>
|
||
|
|
||
|
#ifdef _MSC_VER
|
||
|
#define strncasecmp _strnicmp
|
||
|
#define strcasecmp _stricmp
|
||
|
#else
|
||
|
#include <strings.h>
|
||
|
#endif
|
||
|
|
||
|
static int _ready = 0;
|
||
|
|
||
|
void par_easycurl_init(unsigned int flags)
|
||
|
{
|
||
|
if (!_ready) {
|
||
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||
|
_ready = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void par_easycurl_shutdown()
|
||
|
{
|
||
|
if (_ready) {
|
||
|
curl_global_cleanup();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static size_t onheader(void* v, size_t size, size_t nmemb)
|
||
|
{
|
||
|
size_t n = size * nmemb;
|
||
|
char* h = (char*) v;
|
||
|
if (n > 14 && !strncasecmp("Last-Modified:", h, 14)) {
|
||
|
char const* s = h + 14;
|
||
|
time_t r = curl_getdate(s, 0);
|
||
|
if (r != -1) {
|
||
|
// TODO handle last-modified
|
||
|
}
|
||
|
} else if (n > 5 && !strncasecmp("ETag:", h, 5)) {
|
||
|
// TODO handle etag
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
typedef struct {
|
||
|
par_byte* data;
|
||
|
int nbytes;
|
||
|
} par_easycurl_buffer;
|
||
|
|
||
|
static size_t onwrite(char* contents, size_t size, size_t nmemb, void* udata)
|
||
|
{
|
||
|
size_t realsize = size * nmemb;
|
||
|
par_easycurl_buffer* mem = (par_easycurl_buffer*) udata;
|
||
|
mem->data = (par_byte*) realloc(mem->data, mem->nbytes + realsize + 1);
|
||
|
if (!mem->data) {
|
||
|
return 0;
|
||
|
}
|
||
|
memcpy(mem->data + mem->nbytes, contents, realsize);
|
||
|
mem->nbytes += realsize;
|
||
|
mem->data[mem->nbytes] = 0;
|
||
|
return realsize;
|
||
|
}
|
||
|
|
||
|
#if IOS_EXAMPLE
|
||
|
bool curlToMemory(char const* url, uint8_t** data, int* nbytes)
|
||
|
{
|
||
|
NSString* nsurl =
|
||
|
[NSString stringWithCString:url encoding:NSASCIIStringEncoding];
|
||
|
NSMutableURLRequest* request =
|
||
|
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:nsurl]];
|
||
|
[request setTimeoutInterval: TIMEOUT_SECONDS];
|
||
|
NSURLResponse* response = nil;
|
||
|
NSError* error = nil;
|
||
|
// Use the simple non-async API because we're in a secondary thread anyway.
|
||
|
NSData* nsdata = [NSURLConnection sendSynchronousRequest:request
|
||
|
returningResponse:&response
|
||
|
error:&error];
|
||
|
if (error == nil) {
|
||
|
*nbytes = (int) [nsdata length];
|
||
|
*data = (uint8_t*) malloc([nsdata length]);
|
||
|
memcpy(*data, [nsdata bytes], [nsdata length]);
|
||
|
return true;
|
||
|
}
|
||
|
BLAZE_ERROR("%s\n", [[error localizedDescription] UTF8String]);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
int par_easycurl_to_memory(char const* url, par_byte** data, int* nbytes)
|
||
|
{
|
||
|
char errbuf[CURL_ERROR_SIZE] = {0};
|
||
|
par_easycurl_buffer buffer = {(par_byte*) malloc(1), 0};
|
||
|
long code = 0;
|
||
|
long status = 0;
|
||
|
CURL* handle = curl_easy_init();
|
||
|
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
|
||
|
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
|
||
|
curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, onwrite);
|
||
|
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buffer);
|
||
|
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
|
||
|
curl_easy_setopt(handle, CURLOPT_URL, url);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
|
||
|
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
|
||
|
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errbuf);
|
||
|
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0);
|
||
|
CURLcode res = curl_easy_perform(handle);
|
||
|
if (res != CURLE_OK) {
|
||
|
printf("CURL Error: %s\n", errbuf);
|
||
|
return 0;
|
||
|
}
|
||
|
curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
|
||
|
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
|
||
|
if (status == 304 || status >= 400) {
|
||
|
return 0;
|
||
|
}
|
||
|
*data = buffer.data;
|
||
|
*nbytes = buffer.nbytes;
|
||
|
curl_easy_cleanup(handle);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int par_easycurl_to_file(char const* srcurl, char const* dstpath)
|
||
|
{
|
||
|
long code = 0;
|
||
|
long status = 0;
|
||
|
FILE* filehandle = fopen(dstpath, "wb");
|
||
|
if (!filehandle) {
|
||
|
printf("Unable to open %s for writing.\n", dstpath);
|
||
|
return 0;
|
||
|
}
|
||
|
CURL* handle = curl_easy_init();
|
||
|
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_ENCODING, "gzip, deflate");
|
||
|
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 8);
|
||
|
curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
|
||
|
curl_easy_setopt(handle, CURLOPT_WRITEDATA, filehandle);
|
||
|
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, onheader);
|
||
|
curl_easy_setopt(handle, CURLOPT_URL, srcurl);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMEVALUE, 0);
|
||
|
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, 0);
|
||
|
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 60);
|
||
|
curl_easy_perform(handle);
|
||
|
curl_easy_getinfo(handle, CURLINFO_CONDITION_UNMET, &code);
|
||
|
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
|
||
|
fclose(filehandle);
|
||
|
if (status == 304 || status >= 400) {
|
||
|
remove(dstpath);
|
||
|
return 0;
|
||
|
}
|
||
|
curl_easy_cleanup(handle);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#endif // PAR_EASYCURL_IMPLEMENTATION
|
||
|
#endif // PAR_EASYCURL_H
|
||
|
|
||
|
// par_easycurl is distributed under the MIT license:
|
||
|
//
|
||
|
// Copyright (c) 2019 Philip Rideout
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
// SOFTWARE.
|