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.

191 lines
4.6KB

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