// 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 #include #include #include #ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #else #include #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.