#include "network.hpp" #define CURL_STATICLIB #include #include namespace rack { namespace network { static size_t writeStringCallback(char *ptr, size_t size, size_t nmemb, void *userdata) { std::string *str = (std::string*) userdata; size_t len = size * nmemb; str->append(ptr, len); return len; } json_t *requestJson(Method method, std::string url, json_t *dataJ) { CURL *curl = curl_easy_init(); assert(curl); char *reqStr = NULL; // Process data if (dataJ) { if (method == METHOD_GET) { // Append ?key=value&... to url url += "?"; bool isFirst = true; const char *key; json_t *value; json_object_foreach(dataJ, key, value) { if (json_is_string(value)) { if (!isFirst) url += "&"; url += key; url += "="; const char *str = json_string_value(value); size_t len = json_string_length(value); char *escapedStr = curl_easy_escape(curl, str, len); url += escapedStr; curl_free(escapedStr); isFirst = false; } } } else { reqStr = json_dumps(dataJ, 0); } } curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // Set HTTP method switch (method) { case METHOD_GET: // This is CURL's default break; case METHOD_POST: curl_easy_setopt(curl, CURLOPT_POST, true); break; case METHOD_PUT: curl_easy_setopt(curl, CURLOPT_PUT, true); break; case METHOD_DELETE: curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); break; } // Set headers struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append(headers, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // Body callbacks if (reqStr) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqStr); std::string resText; curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText); // Perform request // INFO("Requesting %s", url.c_str()); // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); CURLcode res = curl_easy_perform(curl); // Cleanup if (reqStr) free(reqStr); curl_easy_cleanup(curl); curl_slist_free_all(headers); if (res == CURLE_OK) { // Parse JSON response json_error_t error; json_t *rootJ = json_loads(resText.c_str(), 0, &error); return rootJ; } return NULL; } static int xferInfoCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { float *progress = (float*) clientp; if (progress) { if (dltotal <= 0) *progress = 1.0; else *progress = (float)dlnow / dltotal; } return 0; } bool requestDownload(std::string url, const std::string &filename, float *progress) { if (progress) *progress = 0.f; CURL *curl = curl_easy_init(); if (!curl) return false; FILE *file = fopen(filename.c_str(), "wb"); if (!file) return false; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_VERBOSE, false); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferInfoCallback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, progress); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // Fail on 4xx and 5xx HTTP codes curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); CURLcode res = curl_easy_perform(curl); curl_easy_cleanup(curl); fclose(file); if (res != CURLE_OK) remove(filename.c_str()); return res == CURLE_OK; } std::string encodeUrl(const std::string &s) { CURL *curl = curl_easy_init(); assert(curl); char *escaped = curl_easy_escape(curl, s.c_str(), s.size()); std::string ret = escaped; curl_free(escaped); curl_easy_cleanup(curl); return ret; } std::string computeSHA256File(const std::string &filename) { FILE *f = fopen(filename.c_str(), "rb"); if (!f) return ""; uint8_t hash[SHA256_DIGEST_LENGTH]; SHA256_CTX sha256; SHA256_Init(&sha256); const int bufferLen = 1 << 15; uint8_t *buffer = new uint8_t[bufferLen]; int len = 0; while ((len = fread(buffer, 1, bufferLen, f))) { SHA256_Update(&sha256, buffer, len); } SHA256_Final(hash, &sha256); delete[] buffer; fclose(f); // Convert binary hash to hex char hashHex[64]; const char hexTable[] = "0123456789abcdef"; for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) { uint8_t h = hash[i]; hashHex[2*i + 0] = hexTable[h >> 4]; hashHex[2*i + 1] = hexTable[h & 0x0f]; } std::string str(hashHex, sizeof(hashHex)); return str; } } // namespace network } // namespace rack