l_http.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "lua_api/l_internal.h"
  17. #include "common/c_converter.h"
  18. #include "common/c_content.h"
  19. #include "lua_api/l_http.h"
  20. #include "httpfetch.h"
  21. #include "settings.h"
  22. #include "debug.h"
  23. #include "log.h"
  24. #include <algorithm>
  25. #include <iomanip>
  26. #include <cctype>
  27. #define HTTP_API(name) \
  28. lua_pushstring(L, #name); \
  29. lua_pushcfunction(L, l_http_##name); \
  30. lua_settable(L, -3);
  31. #if USE_CURL
  32. void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req)
  33. {
  34. luaL_checktype(L, 1, LUA_TTABLE);
  35. req.caller = httpfetch_caller_alloc_secure();
  36. getstringfield(L, 1, "url", req.url);
  37. getstringfield(L, 1, "user_agent", req.useragent);
  38. req.multipart = getboolfield_default(L, 1, "multipart", false);
  39. if (getintfield(L, 1, "timeout", req.timeout))
  40. req.timeout *= 1000;
  41. lua_getfield(L, 1, "method");
  42. if (lua_isstring(L, -1)) {
  43. std::string mth = getstringfield_default(L, 1, "method", "");
  44. if (mth == "GET")
  45. req.method = HTTP_GET;
  46. else if (mth == "POST")
  47. req.method = HTTP_POST;
  48. else if (mth == "PUT")
  49. req.method = HTTP_PUT;
  50. else if (mth == "DELETE")
  51. req.method = HTTP_DELETE;
  52. }
  53. lua_pop(L, 1);
  54. // post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead
  55. lua_getfield(L, 1, "post_data");
  56. if (lua_isnil(L, 2)) {
  57. lua_pop(L, 1);
  58. lua_getfield(L, 1, "data");
  59. }
  60. else {
  61. req.method = HTTP_POST;
  62. }
  63. if (lua_istable(L, 2)) {
  64. lua_pushnil(L);
  65. while (lua_next(L, 2) != 0) {
  66. req.fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1);
  67. lua_pop(L, 1);
  68. }
  69. } else if (lua_isstring(L, 2)) {
  70. req.raw_data = readParam<std::string>(L, 2);
  71. }
  72. lua_pop(L, 1);
  73. lua_getfield(L, 1, "extra_headers");
  74. if (lua_istable(L, 2)) {
  75. lua_pushnil(L);
  76. while (lua_next(L, 2) != 0) {
  77. req.extra_headers.emplace_back(readParam<std::string>(L, -1));
  78. lua_pop(L, 1);
  79. }
  80. }
  81. lua_pop(L, 1);
  82. }
  83. void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed)
  84. {
  85. lua_newtable(L);
  86. setboolfield(L, -1, "succeeded", res.succeeded);
  87. setboolfield(L, -1, "timeout", res.timeout);
  88. setboolfield(L, -1, "completed", completed);
  89. setintfield(L, -1, "code", res.response_code);
  90. setstringfield(L, -1, "data", res.data);
  91. }
  92. // http_api.fetch_sync(HTTPRequest definition)
  93. int ModApiHttp::l_http_fetch_sync(lua_State *L)
  94. {
  95. NO_MAP_LOCK_REQUIRED;
  96. HTTPFetchRequest req;
  97. read_http_fetch_request(L, req);
  98. infostream << "Mod performs HTTP request with URL " << req.url << std::endl;
  99. HTTPFetchResult res;
  100. httpfetch_sync(req, res);
  101. push_http_fetch_result(L, res, true);
  102. return 1;
  103. }
  104. // http_api.fetch_async(HTTPRequest definition)
  105. int ModApiHttp::l_http_fetch_async(lua_State *L)
  106. {
  107. NO_MAP_LOCK_REQUIRED;
  108. HTTPFetchRequest req;
  109. read_http_fetch_request(L, req);
  110. infostream << "Mod performs HTTP request with URL " << req.url << std::endl;
  111. httpfetch_async(req);
  112. // Convert handle to hex string since lua can't handle 64-bit integers
  113. std::stringstream handle_conversion_stream;
  114. handle_conversion_stream << std::hex << req.caller;
  115. std::string caller_handle(handle_conversion_stream.str());
  116. lua_pushstring(L, caller_handle.c_str());
  117. return 1;
  118. }
  119. // http_api.fetch_async_get(handle)
  120. int ModApiHttp::l_http_fetch_async_get(lua_State *L)
  121. {
  122. NO_MAP_LOCK_REQUIRED;
  123. std::string handle_str = luaL_checkstring(L, 1);
  124. // Convert hex string back to 64-bit handle
  125. u64 handle;
  126. std::stringstream handle_conversion_stream;
  127. handle_conversion_stream << std::hex << handle_str;
  128. handle_conversion_stream >> handle;
  129. HTTPFetchResult res;
  130. bool completed = httpfetch_async_get(handle, res);
  131. push_http_fetch_result(L, res, completed);
  132. return 1;
  133. }
  134. int ModApiHttp::l_request_http_api(lua_State *L)
  135. {
  136. NO_MAP_LOCK_REQUIRED;
  137. // We have to make sure that this function is being called directly by
  138. // a mod, otherwise a malicious mod could override this function and
  139. // steal its return value.
  140. lua_Debug info;
  141. // Make sure there's only one item below this function on the stack...
  142. if (lua_getstack(L, 2, &info)) {
  143. return 0;
  144. }
  145. FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
  146. FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
  147. // ...and that that item is the main file scope.
  148. if (strcmp(info.what, "main") != 0) {
  149. return 0;
  150. }
  151. // Mod must be listed in secure.http_mods or secure.trusted_mods
  152. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
  153. if (!lua_isstring(L, -1)) {
  154. return 0;
  155. }
  156. std::string mod_name = readParam<std::string>(L, -1);
  157. std::string http_mods = g_settings->get("secure.http_mods");
  158. http_mods.erase(std::remove(http_mods.begin(), http_mods.end(), ' '), http_mods.end());
  159. std::vector<std::string> mod_list_http = str_split(http_mods, ',');
  160. std::string trusted_mods = g_settings->get("secure.trusted_mods");
  161. trusted_mods.erase(std::remove(trusted_mods.begin(), trusted_mods.end(), ' '), trusted_mods.end());
  162. std::vector<std::string> mod_list_trusted = str_split(trusted_mods, ',');
  163. mod_list_http.insert(mod_list_http.end(), mod_list_trusted.begin(), mod_list_trusted.end());
  164. if (std::find(mod_list_http.begin(), mod_list_http.end(), mod_name) == mod_list_http.end()) {
  165. lua_pushnil(L);
  166. return 1;
  167. }
  168. lua_getglobal(L, "core");
  169. lua_getfield(L, -1, "http_add_fetch");
  170. lua_newtable(L);
  171. HTTP_API(fetch_async);
  172. HTTP_API(fetch_async_get);
  173. // Stack now looks like this:
  174. // <core.http_add_fetch> <table with fetch_async, fetch_async_get>
  175. // Now call core.http_add_fetch to append .fetch(request, callback) to table
  176. lua_call(L, 1, 1);
  177. return 1;
  178. }
  179. int ModApiHttp::l_get_http_api(lua_State *L)
  180. {
  181. NO_MAP_LOCK_REQUIRED;
  182. lua_newtable(L);
  183. HTTP_API(fetch_async);
  184. HTTP_API(fetch_async_get);
  185. HTTP_API(fetch_sync);
  186. return 1;
  187. }
  188. #endif
  189. void ModApiHttp::Initialize(lua_State *L, int top)
  190. {
  191. #if USE_CURL
  192. bool isMainmenu = false;
  193. #ifndef SERVER
  194. isMainmenu = ModApiBase::getGuiEngine(L) != nullptr;
  195. #endif
  196. if (isMainmenu) {
  197. API_FCT(get_http_api);
  198. } else {
  199. API_FCT(request_http_api);
  200. }
  201. #endif
  202. }
  203. void ModApiHttp::InitializeAsync(lua_State *L, int top)
  204. {
  205. #if USE_CURL
  206. API_FCT(get_http_api);
  207. #endif
  208. }