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.

194 lines
4.6KB

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