diff --git a/src/gui/lua/Makefile.am b/src/gui/lua/Makefile.am index 90358b08a..c5471750f 100644 --- a/src/gui/lua/Makefile.am +++ b/src/gui/lua/Makefile.am @@ -36,6 +36,7 @@ libneutrino_gui_lua_a_SOURCES = \ lua_cc_text.cpp \ lua_cc_window.cpp \ lua_configfile.cpp \ + lua_curl.cpp \ lua_hintbox.cpp \ lua_menue.cpp \ lua_messagebox.cpp \ diff --git a/src/gui/lua/lua_curl.cpp b/src/gui/lua/lua_curl.cpp new file mode 100644 index 000000000..f25549af5 --- /dev/null +++ b/src/gui/lua/lua_curl.cpp @@ -0,0 +1,352 @@ +/* + * simple lua curl functions + * + * (C) 2015 M. Liebmann (micha-bbg) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "luainstance.h" +#include "lua_curl.h" + +CLuaInstCurl* CLuaInstCurl::getInstance() +{ + static CLuaInstCurl* LuaInstCurl = NULL; + + if (!LuaInstCurl) + LuaInstCurl = new CLuaInstCurl(); + return LuaInstCurl; +} + +CLuaCurl *CLuaInstCurl::CurlCheckData(lua_State *L, int n) +{ + return *(CLuaCurl **) luaL_checkudata(L, n, LUA_CURL_CLASSNAME); +} + +void CLuaInstCurl::LuaCurlRegister(lua_State *L) +{ + luaL_Reg meth[] = { + { "new", CLuaInstCurl::CurlNew }, + { "download", CLuaInstCurl::CurlDownload }, + { "__gc", CLuaInstCurl::CurlDelete }, + { NULL, NULL } + }; + + luaL_newmetatable(L, LUA_CURL_CLASSNAME); + luaL_setfuncs(L, meth, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -1, "__index"); + lua_setglobal(L, LUA_CURL_CLASSNAME); +} + +int CLuaInstCurl::CurlNew(lua_State *L) +{ + CLuaCurl **udata = (CLuaCurl **) lua_newuserdata(L, sizeof(CLuaCurl *)); + *udata = new CLuaCurl(); + luaL_getmetatable(L, LUA_CURL_CLASSNAME); + lua_setmetatable(L, -2); + return 1; +} + +size_t CLuaInstCurl::CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data) +{ + if (size * nmemb > 0) { + std::string* pStr = (std::string*) data; + pStr->append((char*) ptr, nmemb); + } + return size*nmemb; +} + +int CLuaInstCurl::CurlProgressFunc(void *p, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/) +{ + if (dltotal <= 0.0) + return 0; + + struct progressData *_pgd = static_cast(p); + if (_pgd->last_dlnow == dlnow) + return 0; + _pgd->last_dlnow = dlnow; + + double dlSpeed; + long responseCode = 0; + curl_easy_getinfo(_pgd->curl, CURLINFO_SPEED_DOWNLOAD, &dlSpeed); + curl_easy_getinfo(_pgd->curl, CURLINFO_RESPONSE_CODE, &responseCode); + + double dlFragment = dlnow / dltotal; + if (responseCode != 200) { + dlFragment = 0; + dlSpeed = 0; + } + + int showDots = 50; + int dots = round(dlFragment * showDots); + printf(" %3.0f%% [", dlFragment * 100); + int i = 0; + for (; i < dots-1; i++) + printf("="); + if (i < showDots) { + printf(">"); + i++; + } + for (; i < showDots; i++) + printf(" "); + printf("] speed: %.03f KB/sec \r", dlSpeed/1024); fflush(stdout); + + return 0; +} + +int CLuaInstCurl::CurlDownload(lua_State *L) +{ +/* + parameter typ default + ---------------------------------------- + url string required + o, outputfile string when empty then save to string + as secund return value + A, userAgent string empty + v, verbose bool false + s, silent bool false + connectTimeout number 20 + ipv4 bool false + ipv6 bool false + useProxy bool true (default) + followRedir bool true + maxRedirs number 20 +*/ + +/* +Example: + -- simplest program call: + -- ---------------------- + local curl = curl.new() + local ret, data = curl:download{url="http://example.com", o="/tmp/test.txt"} + if ret ~= CURL.OK then + print("Error: " .. data) + end + + -- or -- + + local curl = curl.new() + local ret, data = curl:download{url="http://example.com"} + if ret == CURL.OK then + -- downloaded data + print(data) + .. + else + print("Error: " .. data) + end +*/ + +#define CURL_MSG_ERROR "[curl:download \33[1;31mERROR!\33[0m]" + + lua_assert(lua_istable(L,1)); + + char errMsg[1024]; + CURL *curl_handle = curl_easy_init(); + if (!curl_handle) { + memset(errMsg, '\0', sizeof(errMsg)); + snprintf(errMsg, sizeof(errMsg)-1, "error creating cUrl handle."); + printf("%s %s\n", CURL_MSG_ERROR, errMsg); + lua_pushinteger(L, LUA_CURL_ERR_HANDLE); + lua_pushstring(L, errMsg); + return 2; + } + + std::string url = ""; + tableLookup(L, "url", url); + if (url.empty()) { + curl_easy_cleanup(curl_handle); + memset(errMsg, '\0', sizeof(errMsg)); + snprintf(errMsg, sizeof(errMsg)-1, "no url given."); + printf("%s %s\n", CURL_MSG_ERROR, errMsg); + lua_pushinteger(L, LUA_CURL_ERR_NO_URL); + lua_pushstring(L, errMsg); + return 2; + } + + std::string outputfile = ""; + bool toFile = tableLookup(L, "o", outputfile) || tableLookup(L, "outputfile", outputfile); + std::string retString = ""; + FILE *fp = NULL; + if (toFile) { + fp = fopen(outputfile.c_str(), "wb"); + if (fp == NULL) { + memset(errMsg, '\0', sizeof(errMsg)); + snprintf(errMsg, sizeof(errMsg)-1, "Can't create %s", outputfile.c_str()); + printf("%s %s\n", CURL_MSG_ERROR, errMsg); + curl_easy_cleanup(curl_handle); + lua_pushinteger(L, LUA_CURL_ERR_CREATE_FILE); + lua_pushstring(L, errMsg); + return 2; + } + } + + std::string userAgent = ""; + tableLookup(L, "A", userAgent) || tableLookup(L, "userAgent", userAgent); + + bool verbose = false; + tableLookup(L, "v", verbose) || tableLookup(L, "verbose", verbose); + + bool silent = false; + if (!verbose) + tableLookup(L, "s", silent) || tableLookup(L, "silent", silent); + + lua_Integer connectTimeout = 20; + tableLookup(L, "connectTimeout", connectTimeout); + + bool ipv4 = false; + tableLookup(L, "ipv4", ipv4); + bool ipv6 = false; + tableLookup(L, "ipv6", ipv6); + + bool useProxy = true; + tableLookup(L, "useProxy", useProxy); + + bool followRedir = true; + tableLookup(L, "followRedir", followRedir); + + lua_Integer maxRedirs = 20; + tableLookup(L, "maxRedirs", maxRedirs); + + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + if (toFile) { + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, fp); + } + else { + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, &CLuaInstCurl::CurlWriteToString); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void*)&retString); + } + curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, (long)(4*connectTimeout)); + curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, (long)connectTimeout); + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); + + if (!userAgent.empty()) + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, userAgent.c_str()); + + if (ipv4 && ipv6) + curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER); + else if (ipv4) + curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + else if (ipv6) + curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + + if (!g_settings.softupdate_proxyserver.empty() && useProxy) { + curl_easy_setopt(curl_handle, CURLOPT_PROXY, g_settings.softupdate_proxyserver.c_str()); + if (!g_settings.softupdate_proxyusername.empty()) { + std::string tmp = g_settings.softupdate_proxyusername + ":" + g_settings.softupdate_proxypassword; + curl_easy_setopt(curl_handle, CURLOPT_PROXYUSERPWD, tmp.c_str()); + } + } + + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, (followRedir)?1L:0L); + curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, (long)maxRedirs); + curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, (silent)?1L:0L); + curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, (verbose)?1L:0L); + + progressData pgd; + + if (!silent) { + curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, CLuaInstCurl::CurlProgressFunc); + pgd.curl = curl_handle; + pgd.last_dlnow = -1; + curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, &pgd); + } + + char cerror[CURL_ERROR_SIZE]; + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, cerror); + + printf("\n[curl:download] download %s => %s\n", url.c_str(), (toFile)?outputfile.c_str():"return string"); + if (!silent) + printf("\e[?25l"); /* cursor off */ + printf("\n"); + CURLcode ret = curl_easy_perform(curl_handle); + if (!silent) + printf("\e[?25h"); /* cursor on */ + printf("\n"); + + std::string msg; + if (!silent) { + double dsize, dtime; + char *dredirect=NULL, *deffektive=NULL; + curl_easy_getinfo(curl_handle, CURLINFO_SIZE_DOWNLOAD, &dsize); + curl_easy_getinfo(curl_handle, CURLINFO_TOTAL_TIME, &dtime); + CURLcode res1 = curl_easy_getinfo(curl_handle, CURLINFO_EFFECTIVE_URL, &deffektive); + CURLcode res2 = curl_easy_getinfo(curl_handle, CURLINFO_REDIRECT_URL, &dredirect); + + char msg1[1024]; + memset(msg1, '\0', sizeof(msg1)); + snprintf(msg1, sizeof(msg1)-1, "\n[curl:download] O.K. size: %.0f bytes, time: %.02f sec.", dsize, dtime); + msg = msg1; + if (toFile) + msg += std::string("\n file: ") + outputfile; + else + msg += std::string("\n output: return string"); + msg += std::string("\n url: ") + url; + if ((res1 == CURLE_OK) && deffektive) { + std::string tmp1 = std::string(deffektive); + std::string tmp2 = url; + if (trim(tmp1, " /") != trim(tmp2, " /")) + msg += std::string("\n effektive url: ") + deffektive; + } + if ((res2 == CURLE_OK) && dredirect) + msg += std::string("\n redirect to: ") + dredirect; + } + + curl_easy_cleanup(curl_handle); + if (toFile) + fclose(fp); + + if (ret != 0) { + memset(errMsg, '\0', sizeof(errMsg)); + snprintf(errMsg, sizeof(errMsg)-1, "%s", cerror); + printf("%s curl error: %s\n", CURL_MSG_ERROR, errMsg); + if (toFile) + unlink(outputfile.c_str()); + lua_pushinteger(L, LUA_CURL_ERR_CURL); + lua_pushstring(L, errMsg); + return 2; + } + + if (silent == false) + printf("%s\n \n", msg.c_str()); + + lua_pushinteger(L, LUA_CURL_OK); + lua_pushstring(L, retString.c_str()); + return 2; +} + +int CLuaInstCurl::CurlDelete(lua_State *L) +{ + CLuaCurl *D = CurlCheckData(L, 1); + delete D; + return 0; +} diff --git a/src/gui/lua/lua_curl.h b/src/gui/lua/lua_curl.h new file mode 100644 index 000000000..67b00eec4 --- /dev/null +++ b/src/gui/lua/lua_curl.h @@ -0,0 +1,60 @@ +/* + * simple lua curl functions + * + * (C) 2015 M. Liebmann (micha-bbg) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _LUACURL_H +#define _LUACURL_H + +class CLuaCurl +{ + public: + CLuaCurl() {}; + ~CLuaCurl() {}; +}; + +struct progressData { + CURL *curl; + double last_dlnow; +}; + +class CLuaInstCurl +{ + public: + enum { + LUA_CURL_OK = 0, + LUA_CURL_ERR_HANDLE = 1, + LUA_CURL_ERR_NO_URL = 2, + LUA_CURL_ERR_CREATE_FILE = 3, + LUA_CURL_ERR_CURL = 4 + }; + + CLuaInstCurl() {}; + ~CLuaInstCurl() {}; + static CLuaInstCurl* getInstance(); + static void LuaCurlRegister(lua_State *L); + + private: + static CLuaCurl *CurlCheckData(lua_State *L, int n); + static int CurlNew(lua_State *L); + static size_t CurlWriteToString(void *ptr, size_t size, size_t nmemb, void *data); + static int CurlProgressFunc(void *p, double dltotal, double dlnow, double ultotal, double ulnow); + static int CurlDownload(lua_State *L); + static int CurlDelete(lua_State *L); +}; + +#endif //_LUACURL_H diff --git a/src/gui/lua/luainstance.cpp b/src/gui/lua/luainstance.cpp index 7e34b3510..bfba9584f 100644 --- a/src/gui/lua/luainstance.cpp +++ b/src/gui/lua/luainstance.cpp @@ -41,6 +41,7 @@ #include "lua_cc_text.h" #include "lua_cc_window.h" #include "lua_configfile.h" +#include "lua_curl.h" #include "lua_hintbox.h" #include "lua_menue.h" #include "lua_messagebox.h" @@ -306,6 +307,17 @@ static void set_lua_variables(lua_State *L) { NULL, 0 } }; + + table_key curl_status[] = + { + { "OK", (lua_Integer)CLuaInstCurl::LUA_CURL_OK }, + { "ERR_HANDLE", (lua_Integer)CLuaInstCurl::LUA_CURL_ERR_HANDLE }, + { "ERR_NO_URL", (lua_Integer)CLuaInstCurl::LUA_CURL_ERR_NO_URL }, + { "ERR_CREATE_FILE", (lua_Integer)CLuaInstCurl::LUA_CURL_ERR_CREATE_FILE }, + { "ERR_CURL", (lua_Integer)CLuaInstCurl::LUA_CURL_ERR_CURL }, + { NULL, 0 } + }; + /* list of environment variable arrays to be exported */ lua_envexport e[] = { @@ -318,6 +330,7 @@ static void set_lua_variables(lua_State *L) { "PLAYSTATE", playstate }, { "CC", ccomponents }, { "DYNFONT", dynfont }, + { "CURL", curl_status }, { NULL, NULL } }; @@ -556,6 +569,7 @@ void CLuaInstance::registerFunctions() CLuaInstCCText::getInstance()->CCTextRegister(lua); CLuaInstCCWindow::getInstance()->CCWindowRegister(lua); CLuaInstConfigFile::getInstance()->LuaConfigFileRegister(lua); + CLuaInstCurl::getInstance()->LuaCurlRegister(lua); CLuaInstHintbox::getInstance()->HintboxRegister(lua); CLuaInstMenu::getInstance()->MenuRegister(lua); CLuaInstMessagebox::getInstance()->MessageboxRegister(lua); diff --git a/src/gui/lua/luainstance.h b/src/gui/lua/luainstance.h index 5913edb32..a4ff1e561 100644 --- a/src/gui/lua/luainstance.h +++ b/src/gui/lua/luainstance.h @@ -31,7 +31,7 @@ extern "C" { #include "luainstance_helpers.h" #define LUA_API_VERSION_MAJOR 1 -#define LUA_API_VERSION_MINOR 23 +#define LUA_API_VERSION_MINOR 24 /* inspired by Steve Kemp http://www.steve.org.uk/ */ class CLuaInstance diff --git a/src/gui/lua/luainstance_helpers.h b/src/gui/lua/luainstance_helpers.h index c3ac68039..99eaa8cce 100644 --- a/src/gui/lua/luainstance_helpers.h +++ b/src/gui/lua/luainstance_helpers.h @@ -28,6 +28,7 @@ #define LUA_CLASSNAME "neutrino" #define LUA_VIDEO_CLASSNAME "video" #define LUA_MISC_CLASSNAME "misc" +#define LUA_CURL_CLASSNAME "curl" //#define LUA_WIKI "http://wiki.tuxbox.org/....." #define LUA_WIKI "https://slknet.de/wiki/w"