You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
4.7KB

  1. #define CURL_STATICLIB
  2. #include <curl/curl.h>
  3. #include <openssl/sha.h>
  4. #include "util/common.hpp"
  5. #include "network.hpp"
  6. namespace rack {
  7. namespace network {
  8. static size_t writeStringCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
  9. std::string *str = (std::string*) userdata;
  10. size_t len = size * nmemb;
  11. str->append(ptr, len);
  12. return len;
  13. }
  14. json_t *requestJson(Method method, std::string url, json_t *dataJ) {
  15. CURL *curl = curl_easy_init();
  16. assert(curl);
  17. char *reqStr = NULL;
  18. // Process data
  19. if (dataJ) {
  20. if (method == METHOD_GET) {
  21. // Append ?key=value&... to url
  22. url += "?";
  23. bool isFirst = true;
  24. const char *key;
  25. json_t *value;
  26. json_object_foreach(dataJ, key, value) {
  27. if (json_is_string(value)) {
  28. if (!isFirst)
  29. url += "&";
  30. url += key;
  31. url += "=";
  32. const char *str = json_string_value(value);
  33. size_t len = json_string_length(value);
  34. char *escapedStr = curl_easy_escape(curl, str, len);
  35. url += escapedStr;
  36. curl_free(escapedStr);
  37. isFirst = false;
  38. }
  39. }
  40. }
  41. else {
  42. reqStr = json_dumps(dataJ, 0);
  43. }
  44. }
  45. curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  46. // Set HTTP method
  47. switch (method) {
  48. case METHOD_GET:
  49. // This is CURL's default
  50. break;
  51. case METHOD_POST:
  52. curl_easy_setopt(curl, CURLOPT_POST, true);
  53. break;
  54. case METHOD_PUT:
  55. curl_easy_setopt(curl, CURLOPT_PUT, true);
  56. break;
  57. case METHOD_DELETE:
  58. curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
  59. break;
  60. }
  61. // Set headers
  62. struct curl_slist *headers = NULL;
  63. headers = curl_slist_append(headers, "Accept: application/json");
  64. headers = curl_slist_append(headers, "Content-Type: application/json");
  65. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  66. // Body callbacks
  67. if (reqStr)
  68. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, reqStr);
  69. std::string resText;
  70. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
  71. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
  72. curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resText);
  73. // Perform request
  74. // info("Requesting %s", url.c_str());
  75. // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
  76. CURLcode res = curl_easy_perform(curl);
  77. // Cleanup
  78. if (reqStr)
  79. free(reqStr);
  80. curl_easy_cleanup(curl);
  81. curl_slist_free_all(headers);
  82. if (res == CURLE_OK) {
  83. // Parse JSON response
  84. json_error_t error;
  85. json_t *rootJ = json_loads(resText.c_str(), 0, &error);
  86. return rootJ;
  87. }
  88. return NULL;
  89. }
  90. static int xferInfoCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
  91. float *progress = (float*) clientp;
  92. if (progress) {
  93. if (dltotal <= 0)
  94. *progress = 1.0;
  95. else
  96. *progress = (float)dlnow / dltotal;
  97. }
  98. return 0;
  99. }
  100. bool requestDownload(std::string url, std::string filename, float *progress) {
  101. if (progress)
  102. *progress = 0.f;
  103. CURL *curl = curl_easy_init();
  104. if (!curl)
  105. return false;
  106. FILE *file = fopen(filename.c_str(), "wb");
  107. if (!file)
  108. return false;
  109. curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
  110. curl_easy_setopt(curl, CURLOPT_VERBOSE, false);
  111. curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false);
  112. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
  113. curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
  114. curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferInfoCallback);
  115. curl_easy_setopt(curl, CURLOPT_XFERINFODATA, progress);
  116. curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true);
  117. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
  118. // Fail on 4xx and 5xx HTTP codes
  119. curl_easy_setopt(curl, CURLOPT_FAILONERROR, true);
  120. CURLcode res = curl_easy_perform(curl);
  121. curl_easy_cleanup(curl);
  122. fclose(file);
  123. if (res != CURLE_OK)
  124. remove(filename.c_str());
  125. return res == CURLE_OK;
  126. }
  127. std::string encodeUrl(std::string s) {
  128. CURL *curl = curl_easy_init();
  129. assert(curl);
  130. char *escaped = curl_easy_escape(curl, s.c_str(), s.size());
  131. std::string ret = escaped;
  132. curl_free(escaped);
  133. curl_easy_cleanup(curl);
  134. return ret;
  135. }
  136. std::string computeSHA256File(std::string filename) {
  137. FILE *f = fopen(filename.c_str(), "rb");
  138. if (!f)
  139. return "";
  140. uint8_t hash[SHA256_DIGEST_LENGTH];
  141. SHA256_CTX sha256;
  142. SHA256_Init(&sha256);
  143. const int bufferLen = 1 << 15;
  144. uint8_t *buffer = new uint8_t[bufferLen];
  145. int len = 0;
  146. while ((len = fread(buffer, 1, bufferLen, f))) {
  147. SHA256_Update(&sha256, buffer, len);
  148. }
  149. SHA256_Final(hash, &sha256);
  150. delete[] buffer;
  151. fclose(f);
  152. // Convert binary hash to hex
  153. char hashHex[64];
  154. const char hexTable[] = "0123456789abcdef";
  155. for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
  156. uint8_t h = hash[i];
  157. hashHex[2*i + 0] = hexTable[h >> 4];
  158. hashHex[2*i + 1] = hexTable[h & 0x0f];
  159. }
  160. std::string str(hashHex, sizeof(hashHex));
  161. return str;
  162. }
  163. } // namespace network
  164. } // namespace rack