prosperon/source/engine/thirdparty/par/par_easycurl.h
2023-05-13 03:21:11 +00:00

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.