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
6.0 KiB

  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. lua_getfield(L, 1, "user_agent");
  38. if (lua_isstring(L, -1))
  39. req.useragent = getstringfield_default(L, 1, "user_agent", "");
  40. lua_pop(L, 1);
  41. req.multipart = getboolfield_default(L, 1, "multipart", false);
  42. req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000;
  43. // post_data: if table, post form data, otherwise raw data
  44. lua_getfield(L, 1, "post_data");
  45. if (lua_istable(L, 2)) {
  46. lua_pushnil(L);
  47. while (lua_next(L, 2) != 0)
  48. {
  49. req.post_fields[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1);
  50. lua_pop(L, 1);
  51. }
  52. } else if (lua_isstring(L, 2)) {
  53. req.post_data = readParam<std::string>(L, 2);
  54. }
  55. lua_pop(L, 1);
  56. lua_getfield(L, 1, "extra_headers");
  57. if (lua_istable(L, 2)) {
  58. lua_pushnil(L);
  59. while (lua_next(L, 2) != 0)
  60. {
  61. const char *header = luaL_checkstring(L, -1);
  62. req.extra_headers.push_back(header);
  63. lua_pop(L, 1);
  64. }
  65. }
  66. lua_pop(L, 1);
  67. }
  68. void ModApiHttp::push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed)
  69. {
  70. lua_newtable(L);
  71. setboolfield(L, -1, "succeeded", res.succeeded);
  72. setboolfield(L, -1, "timeout", res.timeout);
  73. setboolfield(L, -1, "completed", completed);
  74. setintfield(L, -1, "code", res.response_code);
  75. setstringfield(L, -1, "data", res.data.c_str());
  76. }
  77. // http_api.fetch_async(HTTPRequest definition)
  78. int ModApiHttp::l_http_fetch_async(lua_State *L)
  79. {
  80. NO_MAP_LOCK_REQUIRED;
  81. HTTPFetchRequest req;
  82. read_http_fetch_request(L, req);
  83. actionstream << "Mod performs HTTP request with URL " << req.url << std::endl;
  84. httpfetch_async(req);
  85. // Convert handle to hex string since lua can't handle 64-bit integers
  86. std::stringstream handle_conversion_stream;
  87. handle_conversion_stream << std::hex << req.caller;
  88. std::string caller_handle(handle_conversion_stream.str());
  89. lua_pushstring(L, caller_handle.c_str());
  90. return 1;
  91. }
  92. // http_api.fetch_async_get(handle)
  93. int ModApiHttp::l_http_fetch_async_get(lua_State *L)
  94. {
  95. NO_MAP_LOCK_REQUIRED;
  96. std::string handle_str = luaL_checkstring(L, 1);
  97. // Convert hex string back to 64-bit handle
  98. u64 handle;
  99. std::stringstream handle_conversion_stream;
  100. handle_conversion_stream << std::hex << handle_str;
  101. handle_conversion_stream >> handle;
  102. HTTPFetchResult res;
  103. bool completed = httpfetch_async_get(handle, res);
  104. push_http_fetch_result(L, res, completed);
  105. return 1;
  106. }
  107. int ModApiHttp::l_request_http_api(lua_State *L)
  108. {
  109. NO_MAP_LOCK_REQUIRED;
  110. // We have to make sure that this function is being called directly by
  111. // a mod, otherwise a malicious mod could override this function and
  112. // steal its return value.
  113. lua_Debug info;
  114. // Make sure there's only one item below this function on the stack...
  115. if (lua_getstack(L, 2, &info)) {
  116. return 0;
  117. }
  118. FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
  119. FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
  120. // ...and that that item is the main file scope.
  121. if (strcmp(info.what, "main") != 0) {
  122. return 0;
  123. }
  124. // Mod must be listed in secure.http_mods or secure.trusted_mods
  125. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
  126. if (!lua_isstring(L, -1)) {
  127. return 0;
  128. }
  129. std::string mod_name = readParam<std::string>(L, -1);
  130. std::string http_mods = g_settings->get("secure.http_mods");
  131. http_mods.erase(std::remove(http_mods.begin(), http_mods.end(), ' '), http_mods.end());
  132. std::vector<std::string> mod_list_http = str_split(http_mods, ',');
  133. std::string trusted_mods = g_settings->get("secure.trusted_mods");
  134. trusted_mods.erase(std::remove(trusted_mods.begin(), trusted_mods.end(), ' '), trusted_mods.end());
  135. std::vector<std::string> mod_list_trusted = str_split(trusted_mods, ',');
  136. mod_list_http.insert(mod_list_http.end(), mod_list_trusted.begin(), mod_list_trusted.end());
  137. if (std::find(mod_list_http.begin(), mod_list_http.end(), mod_name) == mod_list_http.end()) {
  138. lua_pushnil(L);
  139. return 1;
  140. }
  141. lua_getglobal(L, "core");
  142. lua_getfield(L, -1, "http_add_fetch");
  143. lua_newtable(L);
  144. HTTP_API(fetch_async);
  145. HTTP_API(fetch_async_get);
  146. // Stack now looks like this:
  147. // <core.http_add_fetch> <table with fetch_async, fetch_async_get>
  148. // Now call core.http_add_fetch to append .fetch(request, callback) to table
  149. lua_call(L, 1, 1);
  150. return 1;
  151. }
  152. #endif
  153. void ModApiHttp::Initialize(lua_State *L, int top)
  154. {
  155. #if USE_CURL
  156. API_FCT(request_http_api);
  157. #endif
  158. }