summaryrefslogtreecommitdiff
path: root/plugins/MirLua/src/Modules
diff options
context:
space:
mode:
authoraunsane <aunsane@gmail.com>2018-05-20 21:10:10 +0300
committeraunsane <aunsane@gmail.com>2018-05-20 21:34:31 +0300
commita955d18f62f335f84e0926f351eb85d78224cba6 (patch)
tree7bd4d30040ec32bba5949dd2245cb68617fb4192 /plugins/MirLua/src/Modules
parent46363eef857b69761f1d6d28da5a53a954f76900 (diff)
MirLua: project reordering
Diffstat (limited to 'plugins/MirLua/src/Modules')
-rw-r--r--plugins/MirLua/src/Modules/m_chat.cpp25
-rw-r--r--plugins/MirLua/src/Modules/m_clist.cpp140
-rw-r--r--plugins/MirLua/src/Modules/m_core.cpp433
-rw-r--r--plugins/MirLua/src/Modules/m_database.cpp691
-rw-r--r--plugins/MirLua/src/Modules/m_genmenu.cpp95
-rw-r--r--plugins/MirLua/src/Modules/m_hotkeys.cpp103
-rw-r--r--plugins/MirLua/src/Modules/m_http.cpp443
-rw-r--r--plugins/MirLua/src/Modules/m_icolib.cpp150
-rw-r--r--plugins/MirLua/src/Modules/m_json.cpp243
-rw-r--r--plugins/MirLua/src/Modules/m_message.cpp73
-rw-r--r--plugins/MirLua/src/Modules/m_options.cpp117
-rw-r--r--plugins/MirLua/src/Modules/m_protocols.cpp281
-rw-r--r--plugins/MirLua/src/Modules/m_sounds.cpp54
-rw-r--r--plugins/MirLua/src/Modules/m_srmm.cpp138
14 files changed, 2986 insertions, 0 deletions
diff --git a/plugins/MirLua/src/Modules/m_chat.cpp b/plugins/MirLua/src/Modules/m_chat.cpp
new file mode 100644
index 0000000000..c1a2a65d9b
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_chat.cpp
@@ -0,0 +1,25 @@
+#include "../stdafx.h"
+
+static luaL_Reg chatApi[] =
+{
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_chat(lua_State *L)
+{
+ luaL_newlib(L, chatApi);
+
+ MT<GCEVENT>(L, "GCEVENT")
+ .Field(&GCEVENT::pszModule, "Module", LUA_TSTRINGA)
+ .Field(&GCEVENT::ptszID, "Id", LUA_TSTRINGW)
+ .Field(&GCEVENT::iType, "Type", LUA_TINTEGER)
+ .Field(&GCEVENT::time, "Timestamp", LUA_TINTEGER)
+ .Field(&GCEVENT::time, "IsMe", LUA_TINTEGER)
+ .Field(&GCEVENT::time, "Flags", LUA_TINTEGER)
+ .Field(&GCEVENT::ptszNick, "Nick", LUA_TSTRINGW)
+ .Field(&GCEVENT::ptszUID, "Uid", LUA_TSTRINGW)
+ .Field(&GCEVENT::ptszStatus, "Status", LUA_TSTRINGW)
+ .Field(&GCEVENT::ptszText, "Text", LUA_TSTRINGW);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_clist.cpp b/plugins/MirLua/src/Modules/m_clist.cpp
new file mode 100644
index 0000000000..5e5b7475b8
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_clist.cpp
@@ -0,0 +1,140 @@
+#include "../stdafx.h"
+
+
+
+void MakeMenuItem(lua_State *L, CMenuItem &mi)
+{
+ mi.langId = CMLuaEnvironment::GetEnvironmentId(L);
+
+ lua_getfield(L, -1, "Flags");
+ mi.flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ if (!(mi.flags & CMIF_UNICODE))
+ mi.flags |= CMIF_UNICODE;
+
+ lua_getfield(L, -1, "Uid");
+ const char* uuid = lua_tostring(L, -1);
+ if (UuidFromStringA((RPC_CSTR)uuid, (UUID*)&mi.uid))
+ UNSET_UID(mi);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Name");
+ mi.name.w = mir_utf8decodeW(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Position");
+ mi.position = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Icon");
+ mi.hIcolibItem = (HANDLE)lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Service");
+ mi.pszService = lua_tostring(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Parent");
+ mi.root = (HGENMENU)lua_touserdata(L, -1);
+ lua_pop(L, 1);
+}
+
+static int clist_AddMainMenuRoot(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ int position = lua_tointeger(L, 2);
+ HANDLE hIcon = (HANDLE)lua_touserdata(L, 3);
+
+ HGENMENU res = Menu_CreateRoot(MO_MAIN, ptrW(Utf8DecodeW(name)), position, hIcon);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int clist_AddMainMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ CMenuItem mi;
+ MakeMenuItem(L, mi);
+
+ HGENMENU res = Menu_AddMainMenuItem(&mi);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int clist_AddContactMenuRoot(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ int position = lua_tointeger(L, 2);
+ HANDLE hIcon = (HANDLE)lua_touserdata(L, 3);
+
+ HGENMENU res = Menu_CreateRoot(MO_MAIN, ptrW(Utf8DecodeW(name)), position, hIcon);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int clist_AddContactMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ CMenuItem mi;
+ MakeMenuItem(L, mi);
+
+ ptrA szProto(mir_utf8decodeA(lua_tostring(L, 2)));
+ HGENMENU res = Menu_AddContactMenuItem(&mi, szProto);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int clist_AddTrayMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ CMenuItem mi;
+ MakeMenuItem(L, mi);
+
+ HGENMENU res = Menu_AddTrayMenuItem(&mi);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static luaL_Reg clistApi[] =
+{
+ { "AddMainMenuRoot", clist_AddMainMenuRoot },
+ { "AddMainMenuItem", clist_AddMainMenuItem },
+
+ { "AddContactMenuRoot", clist_AddContactMenuRoot },
+ { "AddContactMenuItem", clist_AddContactMenuItem },
+
+ { "AddTrayMenuItem", clist_AddTrayMenuItem },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_clist(lua_State *L)
+{
+ luaL_newlib(L, clistApi);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_core.cpp b/plugins/MirLua/src/Modules/m_core.cpp
new file mode 100644
index 0000000000..027ff163ed
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_core.cpp
@@ -0,0 +1,433 @@
+#include "../stdafx.h"
+
+static int core_CreateHookableEvent(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+
+ HANDLE res = CreateHookableEvent(name);
+ if (res != nullptr)
+ lua_pushlightuserdata(L, res);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+int HookEventLuaParam(void *obj, WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ lua_State *L = (lua_State*)obj;
+
+ int ref = param;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+
+ if (wParam)
+ lua_pushlightuserdata(L, (void*)wParam);
+ else
+ lua_pushnil(L);
+
+ if (lParam)
+ lua_pushlightuserdata(L, (void*)lParam);
+ else
+ lua_pushnil(L);
+
+ luaM_pcall(L, 2, 1);
+
+ return lua_tointeger(L, -1);
+}
+
+int HookEventEnvParam(void *obj, WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ CMLuaEnvironment *env = (CMLuaEnvironment*)obj;
+
+ int ref = param;
+ lua_rawgeti(env->L, LUA_REGISTRYINDEX, ref);
+
+ if (wParam)
+ lua_pushlightuserdata(env->L, (void*)wParam);
+ else
+ lua_pushnil(env->L);
+
+ if (lParam)
+ lua_pushlightuserdata(env->L, (void*)lParam);
+ else
+ lua_pushnil(env->L);
+
+ luaM_pcall(env->L, 2, 1);
+
+ return lua_tointeger(env->L, -1);
+}
+
+static int core_HookEvent(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ HANDLE res = nullptr;
+ CMLuaEnvironment *env = CMLuaEnvironment::GetEnvironment(L);
+ if (env) {
+ res = HookEventObjParam(name, HookEventEnvParam, env, ref);
+ if (res)
+ env->AddHookRef(res, ref);
+ }
+ else
+ res = HookEventObjParam(name, HookEventLuaParam, L, ref);
+
+ if (res == nullptr) {
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ lua_pushnil(L);
+
+ return 1;
+ }
+
+ lua_pushlightuserdata(L, res);
+
+ return 1;
+}
+
+static int core_HookTemporaryEvent(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ HANDLE res = nullptr;
+ CMLuaEnvironment *env = CMLuaEnvironment::GetEnvironment(L);
+ if (env) {
+ res = HookEventObjParam(name, HookEventEnvParam, env, ref);
+ if (res)
+ env->AddHookRef(res, ref);
+ }
+ else
+ res = HookEventObjParam(name, HookEventLuaParam, L, ref);
+
+ // event does not exists, call hook immideatelly
+ if (res == nullptr) {
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ lua_pushnil(L);
+ lua_pushnil(L);
+ luaM_pcall(L, 2, 1);
+ return lua_tointeger(env->L, -1);
+ }
+
+ lua_pushlightuserdata(L, res);
+
+ return 1;
+}
+
+static int core_UnhookEvent(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HANDLE hEvent = lua_touserdata(L, 1);
+
+ int res = UnhookEvent(hEvent);
+ if (!res) {
+ CMLuaEnvironment *env = CMLuaEnvironment::GetEnvironment(L);
+ if (env)
+ env->ReleaseHookRef(hEvent);
+ }
+ lua_pushboolean(L, !res);
+
+ return 1;
+}
+
+static int core_NotifyEventHooks(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HANDLE hEvent = lua_touserdata(L, 1);
+ WPARAM wParam = (WPARAM)luaM_tomparam(L, 2);
+ LPARAM lParam = (LPARAM)luaM_tomparam(L, 3);
+
+ int res = NotifyEventHooks(hEvent, wParam, lParam);
+ lua_pushboolean(L, res != -1);
+
+ return 1;
+}
+
+/***********************************************/
+
+INT_PTR CreateServiceFunctionLuaStateParam(void *obj, WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ lua_State *L = (lua_State*)obj;
+
+ int ref = param;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+
+ lua_pushlightuserdata(L, (void*)wParam);
+ lua_pushlightuserdata(L, (void*)lParam);
+ luaM_pcall(L, 2, 1);
+
+ INT_PTR res = lua_tointeger(L, 1);
+ lua_pushinteger(L, res);
+
+ return res;
+}
+
+INT_PTR CreateServiceFunctionEnvParam(void *obj, WPARAM wParam, LPARAM lParam, LPARAM param)
+{
+ CMLuaEnvironment *env = (CMLuaEnvironment*)obj;
+
+ int ref = param;
+ lua_rawgeti(env->L, LUA_REGISTRYINDEX, ref);
+
+ lua_pushlightuserdata(env->L, (void*)wParam);
+ lua_pushlightuserdata(env->L, (void*)lParam);
+ luaM_pcall(env->L, 2, 1);
+
+ INT_PTR res = lua_tointeger(env->L, 1);
+ lua_pushinteger(env->L, res);
+
+ return res;
+}
+
+static int core_CreateServiceFunction(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ luaL_checktype(L, 2, LUA_TFUNCTION);
+
+ lua_pushvalue(L, 2);
+ int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ HANDLE res = nullptr;
+ CMLuaEnvironment *env = CMLuaEnvironment::GetEnvironment(L);
+ if (env) {
+ res = CreateServiceFunctionObjParam(name, CreateServiceFunctionEnvParam, env, ref);
+ if (res)
+ env->AddServiceRef(res, ref);
+ }
+ else
+ res = CreateServiceFunctionObjParam(name, CreateServiceFunctionLuaStateParam, L, ref);
+
+ if (!res) {
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_pushlightuserdata(L, res);
+
+ return 1;
+}
+
+static int core_CallService(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ WPARAM wParam = (WPARAM)luaM_tomparam(L, 2);
+ LPARAM lParam = (LPARAM)luaM_tomparam(L, 3);
+
+ INT_PTR res = CallService(name, wParam, lParam);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int core_ServiceExists(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+
+ int res = ServiceExists(name);
+ lua_pushboolean(L, res);
+
+ return 1;
+}
+
+static int core_DestroyServiceFunction(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HANDLE hService = lua_touserdata(L, 1);
+
+ CMLuaEnvironment *env = CMLuaEnvironment::GetEnvironment(L);
+ if (env)
+ env->ReleaseHookRef(hService);
+
+ DestroyServiceFunction(hService);
+
+ return 0;
+}
+
+/***********************************************/
+
+static int core_IsPluginLoaded(lua_State *L)
+{
+ const char *value = lua_tostring(L, 1);
+
+ MUUID uuid = { 0 };
+ bool res = UuidFromStringA((RPC_CSTR)value, (UUID*)&uuid) == RPC_S_OK;
+ if (res)
+ res = IsPluginLoaded(uuid) > 0;
+ lua_pushboolean(L, res);
+
+ return 1;
+}
+
+static int core_Free(lua_State *L)
+{
+ if (lua_islightuserdata(L, 1))
+ {
+ void *ptr = lua_touserdata(L, 1);
+ mir_free(ptr);
+ }
+
+ return 0;
+}
+
+static int core_Translate(lua_State *L)
+{
+ char *what = (char*)luaL_checkstring(L, 1);
+
+ ptrW value(mir_utf8decodeW(what));
+ lua_pushstring(L, T2Utf(TranslateW_LP(value, hLangpack)));
+
+ return 1;
+}
+
+static int core_Parse(lua_State *L)
+{
+ char *what = (char*)luaL_checkstring(L, 1);
+
+ ptrW value(mir_utf8decodeW(what));
+ lua_pushstring(L, T2Utf(VARSW(value)));
+
+ return 1;
+}
+
+static int core_GetFullPath(lua_State *L)
+{
+ wchar_t path[MAX_PATH];
+ GetModuleFileName(nullptr, path, MAX_PATH);
+
+ lua_pushstring(L, ptrA(mir_utf8encodeW(path)));
+
+ return 1;
+}
+
+/***********************************************/
+
+struct core_ForkThreadParam
+{
+ lua_State *L;
+ int threadRef;
+ int functionRef;
+ HANDLE hThread;
+};
+
+std::map<HANDLE, core_ForkThreadParam*> lstThreads;
+
+void DestroyThread(core_ForkThreadParam *ftp)
+{
+ luaL_unref(ftp->L, LUA_REGISTRYINDEX, ftp->functionRef);
+ luaL_unref(ftp->L, LUA_REGISTRYINDEX, ftp->threadRef);
+ lstThreads.erase(ftp->hThread);
+
+ delete ftp;
+}
+
+void __cdecl ThreadFunc(void *p)
+{
+ core_ForkThreadParam *ftp = (core_ForkThreadParam*)p;
+
+ lua_rawgeti(ftp->L, LUA_REGISTRYINDEX, ftp->functionRef);
+ luaM_pcall(ftp->L, 0, 1);
+ DestroyThread(ftp);
+}
+
+static int core_ForkThread(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ core_ForkThreadParam *p = new core_ForkThreadParam();
+
+ p->L = lua_newthread(L);
+ p->threadRef = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_pushvalue(L, 1);
+ p->functionRef = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ p->hThread = mir_forkthread(ThreadFunc, p);
+ lstThreads[p->hThread] = p;
+ lua_pushlightuserdata(L, p->hThread);
+
+ return 1;
+}
+
+static int core_TerminateThread(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HANDLE hThread = (HANDLE)lua_touserdata(L, 1);
+
+ auto it = lstThreads.find(hThread);
+ if (it != lstThreads.end()) {
+ DestroyThread(it->second);
+ lua_pushboolean(L, TerminateThread(hThread, 0));
+ }
+ else lua_pushboolean(L, 0);
+
+ return 1;
+}
+
+/***********************************************/
+
+luaL_Reg coreApi[] =
+{
+ { "CreateHookableEvent", core_CreateHookableEvent },
+
+ { "NotifyEventHooks", core_NotifyEventHooks },
+
+ { "HookEvent", core_HookEvent },
+ { "HookTemporaryEvent", core_HookTemporaryEvent },
+ { "UnhookEvent", core_UnhookEvent },
+
+ { "CreateServiceFunction", core_CreateServiceFunction },
+ { "DestroyServiceFunction", core_DestroyServiceFunction },
+
+ { "ServiceExists", core_ServiceExists },
+ { "CallService", core_CallService },
+
+ { "IsPluginLoaded", core_IsPluginLoaded },
+
+ { "Free", core_Free },
+
+ { "Translate", core_Translate },
+
+ { "Parse", core_Parse },
+
+ { "GetFullPath", core_GetFullPath },
+
+ { "ForkThread", core_ForkThread },
+ { "TerminateThread", core_TerminateThread },
+
+ { "Version", nullptr },
+
+ { "NULL", nullptr },
+ { "INVALID_HANDLE_VALUE", nullptr },
+ { "CALLSERVICE_NOTFOUND", nullptr },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+LUAMOD_API int luaopen_m_core(lua_State *L)
+{
+ luaL_newlib(L, coreApi);
+ lua_pushlightuserdata(L, nullptr);
+ lua_setfield(L, -2, "NULL");
+ lua_pushlightuserdata(L, INVALID_HANDLE_VALUE);
+ lua_setfield(L, -2, "INVALID_HANDLE_VALUE");
+ lua_pushinteger(L, CALLSERVICE_NOTFOUND);
+ lua_setfield(L, -2, "CALLSERVICE_NOTFOUND");
+
+ char version[128];
+ Miranda_GetVersionText(version, _countof(version));
+ lua_pushstring(L, ptrA(mir_utf8encode(version)));
+ lua_setfield(L, -2, "Version");
+
+ // set copy to global variable m
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, "m");
+
+ return 1;
+} \ No newline at end of file
diff --git a/plugins/MirLua/src/Modules/m_database.cpp b/plugins/MirLua/src/Modules/m_database.cpp
new file mode 100644
index 0000000000..3d0d53ba48
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_database.cpp
@@ -0,0 +1,691 @@
+#include "../stdafx.h"
+
+void luaM_pushdbvt(lua_State *L, const DBVARIANT &value)
+{
+ switch (value.type) {
+ case DBVT_BYTE:
+ lua_pushinteger(L, value.bVal);
+ break;
+ case DBVT_WORD:
+ lua_pushinteger(L,value.wVal);
+ break;
+ case DBVT_DWORD:
+ lua_pushnumber(L, value.dVal);
+ break;
+ case DBVT_ASCIIZ:
+ lua_pushstring(L, ptrA(mir_utf8encode(value.pszVal)));
+ break;
+ case DBVT_UTF8:
+ lua_pushstring(L, value.pszVal);
+ break;
+ case DBVT_WCHAR:
+ lua_pushstring(L, ptrA(mir_utf8encodeW(value.pwszVal)));
+ break;
+ case DBVT_BLOB:
+ {
+ lua_createtable(L, value.cpbVal, 0);
+ for (int i = 0; i < value.cpbVal; i++) {
+ lua_pushinteger(L, value.pbVal[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ }
+ break;
+ default:
+ lua_pushnil(L);
+ }
+}
+
+/***********************************************/
+
+static int db_ContactIterator(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, lua_upvalueindex(1));
+ const char *szModule = lua_tostring(L, lua_upvalueindex(2));
+
+ hContact = hContact == NULL
+ ? db_find_first(szModule)
+ : db_find_next(hContact, szModule);
+
+ if (hContact) {
+ lua_pushinteger(L, hContact);
+ lua_pushvalue(L, -1);
+ lua_replace(L, lua_upvalueindex(1));
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int db_Contacts(lua_State *L)
+{
+ const char *szModule = nullptr;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TNONE:
+ break;
+ case LUA_TSTRING:
+ szModule = lua_tostring(L, 1);
+ break;
+ case LUA_TUSERDATA:
+ {
+ PROTOACCOUNT **pa = (PROTOACCOUNT**)luaL_checkudata(L, 1, MT_PROTOACCOUNT);
+ szModule = (*pa)->szModuleName;
+ break;
+ }
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ lua_pushinteger(L, 0);
+ lua_pushstring(L, szModule);
+ lua_pushcclosure(L, db_ContactIterator, 2);
+
+ return 1;
+}
+
+static const char *mods[] =
+{
+ "FirstName",
+ "LastName",
+ "Nick",
+ "CustomNick",
+ "Email",
+ "City",
+ "State",
+ "Country",
+ "Phone",
+ "Homepage",
+ "About",
+ "Gender",
+ "Age",
+ "FullName",
+ "Uid",
+ "DisplayName",
+ nullptr
+};
+
+static int db_GetContactInfo(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ int type = 0;
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ type = luaL_checkinteger(L, 2);
+ break;
+ case LUA_TSTRING:
+ type = luaL_checkoption(L, 2, nullptr, mods) + 1;
+ break;
+ default:
+ luaL_argerror(L, 2, luaL_typename(L, 2));
+ }
+
+ ptrW value(Contact_GetInfo(type, hContact));
+ if (value)
+ lua_pushstring(L, ptrA(mir_utf8encodeW(value)));
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+/***********************************************/
+
+static int db_GetEventCount(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ int res = db_event_count(hContact);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_GetFirstEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ MEVENT res = db_event_first(hContact);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_GetPrevEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+ MEVENT hDbEvent = luaL_checkinteger(L, 2);
+
+ MEVENT res = db_event_prev(hContact, hDbEvent);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_GetNextEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+ MEVENT hDbEvent = luaL_checkinteger(L, 2);
+
+ MEVENT res = db_event_next(hContact, hDbEvent);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_GetLastEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ MEVENT res = db_event_last(hContact);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_GetFirstUnreadEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ MEVENT res = db_event_firstUnread(hContact);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int db_EventIterator(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, lua_upvalueindex(1));
+ MEVENT hDbEvent = luaL_checkinteger(L, lua_upvalueindex(2));
+
+ hDbEvent = hDbEvent == NULL
+ ? db_event_first(hContact)
+ : db_event_next(hContact, hDbEvent);
+
+ if (hDbEvent) {
+ lua_pushinteger(L, hDbEvent);
+ lua_pushvalue(L, -1);
+ lua_replace(L, lua_upvalueindex(2));
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int db_Events(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ lua_pushinteger(L, hContact);
+ lua_pushinteger(L, NULL);
+ lua_pushcclosure(L, db_EventIterator, 2);
+
+ return 1;
+}
+
+static int db_EventReverseIterator(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, lua_upvalueindex(1));
+ MEVENT hDbEvent = luaL_checkinteger(L, lua_upvalueindex(2));
+
+ hDbEvent = hDbEvent == NULL
+ ? db_event_last(hContact)
+ : db_event_prev(hContact, hDbEvent);
+
+ if (hDbEvent) {
+ lua_pushinteger(L, hDbEvent);
+ lua_pushvalue(L, -1);
+ lua_replace(L, lua_upvalueindex(2));
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int db_EventsFromEnd(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ lua_pushinteger(L, hContact);
+ lua_pushinteger(L, NULL);
+ lua_pushcclosure(L, db_EventReverseIterator, 2);
+
+ return 1;
+}
+
+static int db_UnreadEventIterator(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, lua_upvalueindex(1));
+ MEVENT hDbEvent = luaL_checkinteger(L, lua_upvalueindex(2));
+
+ hDbEvent = db_event_firstUnread(hContact);
+
+ if (hDbEvent) {
+ lua_pushinteger(L, hDbEvent);
+ lua_pushvalue(L, -1);
+ lua_replace(L, lua_upvalueindex(2));
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int db_UnreadEvents(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ lua_pushinteger(L, hContact);
+ lua_pushinteger(L, NULL);
+ lua_pushcclosure(L, db_UnreadEventIterator, 2);
+
+ return 1;
+}
+
+void MakeDbEvent(lua_State *L, DBEVENTINFO &dbei)
+{
+ lua_getfield(L, -1, "Module");
+ dbei.szModule = mir_strdup(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Type");
+ dbei.eventType = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Timestamp");
+ dbei.timestamp = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Flags");
+ dbei.flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Blob");
+ switch (lua_type(L, -1)) {
+ case LUA_TTABLE:
+ dbei.cbBlob = (DWORD)lua_rawlen(L, 4);
+ dbei.pBlob = (BYTE*)mir_calloc(dbei.cbBlob);
+ for (DWORD i = 0; i < dbei.cbBlob; i++) {
+ lua_geti(L, 4, i + 1);
+ dbei.pBlob[i] = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ }
+ break;
+ case LUA_TSTRING:
+ size_t nLen;
+ const char *str = lua_tolstring(L, -1, &nLen);
+ dbei.cbBlob = (DWORD)nLen;
+ dbei.pBlob = (BYTE*)mir_alloc(nLen);
+ memcpy(dbei.pBlob, str, nLen);
+ break;
+ }
+ lua_pop(L, 1);
+}
+
+static int db_AddEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+
+ DBEVENTINFO dbei;
+ MakeDbEvent(L, dbei);
+ MEVENT hDbEvent = db_event_add(hContact, &dbei);
+
+ if (hDbEvent)
+ lua_pushnumber(L, hDbEvent);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int db_MarkReadEvent(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+ MEVENT hDbEvent = luaL_checkinteger(L, 2);
+
+ int res = db_event_markRead(hContact, hDbEvent);
+ lua_pushnumber(L, res);
+
+ return 1;
+}
+
+/***********************************************/
+
+static int ModulesEnumProc(const char *szModuleName, void *lParam)
+{
+ if (szModuleName) {
+ LIST<char>* p = (LIST<char>*)lParam;
+ p->insert(mir_strdup(szModuleName));
+ }
+
+ return 0;
+}
+
+static int db_ModulesIterator(lua_State *L)
+{
+ int i = lua_tointeger(L, lua_upvalueindex(1));
+ LIST<char> &param = *(LIST<char>*)lua_touserdata(L, lua_upvalueindex(2));
+
+ if (i < param.getCount()) {
+ lua_pushinteger(L, (i + 1));
+ lua_replace(L, lua_upvalueindex(1));
+ lua_pushstring(L, ptrA(mir_utf8encode(param[i])));
+ mir_free(param[i]);
+ }
+ else {
+ lua_pushnil(L);
+ delete &param;
+ }
+
+ return 1;
+}
+
+static int db_Modules(lua_State *L)
+{
+ LIST<char> *param = new LIST<char>(5, PtrKeySortT);
+
+ db_enum_modules(ModulesEnumProc, param);
+
+ lua_pushinteger(L, 0);
+ lua_pushlightuserdata(L, param);
+ lua_pushcclosure(L, db_ModulesIterator, 2);
+
+ return 1;
+}
+
+static int db_DeleteModule(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, 1);
+ const char *szModule = luaL_checkstring(L, 2);
+
+ INT_PTR res = db_delete_module(hContact, szModule);
+ lua_pushboolean(L, !res);
+
+ return 1;
+}
+
+static int SettingsEnumProc(const char* szSetting, void *lParam)
+{
+ if (szSetting ) {
+ LIST<char>* p = (LIST<char>*)lParam;
+ p->insert(mir_strdup(szSetting));
+ }
+ return 0;
+}
+
+static int db_SettingIterator(lua_State *L)
+{
+ int i = lua_tointeger(L, lua_upvalueindex(1));
+ LIST<char> &param = *(LIST<char>*)lua_touserdata(L, lua_upvalueindex(2));
+
+ if (i < param.getCount()) {
+ lua_pushinteger(L, (i + 1));
+ lua_replace(L, lua_upvalueindex(1));
+ lua_pushstring(L, ptrA(mir_utf8encode(param[i])));
+ mir_free(param[i]);
+ }
+ else {
+ lua_pushnil(L);
+ delete &param;
+ }
+
+ return 1;
+}
+
+static int db_Settings(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, 1);
+ const char* szModule = luaL_checkstring(L, 2);
+
+ LIST<char> *param = new LIST<char>(5, PtrKeySortT);
+ db_enum_settings(hContact, SettingsEnumProc, szModule, param);
+
+ lua_pushinteger(L, 0);
+ lua_pushlightuserdata(L, param);
+ lua_pushcclosure(L, db_SettingIterator, 2);
+
+ return 1;
+}
+
+static int db_GetSetting(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, 1);
+ const char *szModule = luaL_checkstring(L, 2);
+ const char *szSetting = luaL_checkstring(L, 3);
+
+ DBVARIANT dbv;
+ if (db_get(hContact, szModule, szSetting, &dbv)) {
+ lua_pushvalue(L, 4);
+ return 1;
+ }
+
+ luaM_pushdbvt(L, dbv);
+ db_free(&dbv);
+
+ if (lua_isnil(L, -1) && !lua_isnoneornil(L, 4)) {
+ lua_pop(L, 1);
+ lua_pushvalue(L, 4);
+ }
+
+ return 1;
+}
+
+static int db_WriteSetting(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, 1);
+ const char *szModule = luaL_checkstring(L, 2);
+ const char *szSetting = luaL_checkstring(L, 3);
+ luaL_checkany(L, 4);
+
+ DBVARIANT dbv;
+ if (lua_isnoneornil(L, 5)) {
+ int type = lua_type(L, 4);
+ switch (type) {
+ case LUA_TBOOLEAN:
+ dbv.type = DBVT_BYTE;
+ break;
+ case LUA_TNUMBER:
+ dbv.type = DBVT_DWORD;
+ break;
+ case LUA_TSTRING:
+ dbv.type = DBVT_UTF8;
+ break;
+ case LUA_TTABLE:
+ dbv.type = DBVT_BLOB;
+ break;
+ default:
+ lua_pushboolean(L, false);
+ return 1;
+ }
+ }
+ else
+ dbv.type = luaL_checkinteger(L, 5);
+
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ dbv.bVal = lua_isboolean(L, 4)
+ ? lua_toboolean(L, 4)
+ : luaL_checknumber(L, 4);
+ break;
+ case DBVT_WORD:
+ dbv.wVal = luaL_checknumber(L, 4);
+ break;
+ case DBVT_DWORD:
+ dbv.dVal = luaL_checknumber(L, 4);
+ break;
+ case DBVT_UTF8:
+ dbv.pszVal = mir_strdup(luaL_checkstring(L, 4));
+ break;
+ case DBVT_ASCIIZ:
+ dbv.pszVal = mir_utf8decodeA(luaL_checkstring(L, 4));
+ break;
+ case DBVT_WCHAR:
+ dbv.pwszVal = mir_utf8decodeW(luaL_checkstring(L, 4));
+ break;
+ case DBVT_BLOB:
+ {
+ dbv.cpbVal = (WORD)lua_rawlen(L, 4);
+ dbv.pbVal = (BYTE*)mir_calloc(dbv.cpbVal);
+ for (int i = 0; i < dbv.cpbVal; i++) {
+ lua_geti(L, 4, i + 1);
+ dbv.pbVal[i] = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ }
+ }
+ break;
+ default:
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ INT_PTR res = db_set(hContact, szModule, szSetting, &dbv);
+ lua_pushboolean(L, !res);
+
+ return 1;
+}
+
+static int db_DeleteSetting(lua_State *L)
+{
+ MCONTACT hContact = lua_tointeger(L, 1);
+ LPCSTR szModule = luaL_checkstring(L, 2);
+ LPCSTR szSetting = luaL_checkstring(L, 3);
+
+ INT_PTR res = db_unset(hContact, szModule, szSetting);
+ lua_pushboolean(L, !res);
+
+ return 1;
+}
+
+/***********************************************/
+
+static luaL_Reg databaseApi[] =
+{
+ { "Contacts", db_Contacts },
+ { "GetContactInfo", db_GetContactInfo },
+
+ { "GetEventCount", db_GetEventCount },
+
+ { "GetFirstEvent", db_GetFirstEvent },
+ { "GetPrevEvent", db_GetPrevEvent },
+ { "GetNextEvent", db_GetNextEvent },
+ { "GetLastEvent", db_GetLastEvent },
+ { "GetFirstUnreadEvent", db_GetFirstUnreadEvent },
+ { "Events", db_Events },
+ { "EventsFromEnd", db_EventsFromEnd },
+ { "UnreadEvents", db_UnreadEvents },
+ { "AddEvent", db_AddEvent },
+ { "MarkReadEvent", db_MarkReadEvent },
+
+ { "Settings", db_Settings },
+ { "Modules", db_Modules },
+
+ { "DeleteModule", db_DeleteModule },
+
+ { "GetSetting", db_GetSetting },
+ { "WriteSetting", db_WriteSetting },
+ { "SetSetting", db_WriteSetting },
+ { "DeleteSetting", db_DeleteSetting },
+
+ { "DBVT_BYTE", nullptr },
+ { "DBVT_WORD", nullptr },
+ { "DBVT_DWORD", nullptr },
+ { "DBVT_ASCIIZ", nullptr },
+ { "DBVT_UTF8", nullptr },
+ { "DBVT_WCHAR", nullptr },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+#define MT_DBCONTACTWRITESETTING "DBCONTACTWRITESETTING"
+
+template <>
+int MT<DBCONTACTWRITESETTING>::Get(lua_State *L, DBCONTACTWRITESETTING *dbcw)
+{
+ const char *key = luaL_checkstring(L, 2);
+
+ if (mir_strcmpi(key, "Value") == 0)
+ luaM_pushdbvt(L, dbcw->value);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+/***********************************************/
+
+#define MT_DBEVENTINFO "DBEVENTINFO"
+
+template <>
+DBEVENTINFO* MT<DBEVENTINFO>::Init(lua_State *L)
+{
+ MEVENT hDbEvent = luaL_checkinteger(L, 1);
+
+ DBEVENTINFO* dbei = (DBEVENTINFO*)mir_calloc(sizeof(DBEVENTINFO));
+ dbei->cbBlob = db_event_getBlobSize((MEVENT)hDbEvent);
+ dbei->pBlob = (PBYTE)mir_calloc(dbei->cbBlob);
+ db_event_get((MEVENT)hDbEvent, dbei);
+
+ return dbei;
+}
+
+template <>
+int MT<DBEVENTINFO>::Get(lua_State *L, DBEVENTINFO *dbei)
+{
+ const char *key = luaL_checkstring(L, 2);
+
+ if (mir_strcmpi(key, "Blob") == 0) {
+ lua_createtable(L, dbei->cbBlob, 0);
+ for (DWORD i = 0; i < dbei->cbBlob; i++) {
+ lua_pushinteger(L, dbei->pBlob[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+template <>
+void MT<DBEVENTINFO>::Free(lua_State*, DBEVENTINFO **dbei)
+{
+ mir_free((*dbei)->pBlob);
+ mir_free(*dbei);
+}
+
+/***********************************************/
+
+LUAMOD_API int luaopen_m_database(lua_State *L)
+{
+ luaL_newlib(L, databaseApi);
+
+ lua_pushnumber(L, DBVT_BYTE);
+ lua_setfield(L, -2, "DBVT_BYTE");
+ lua_pushnumber(L, DBVT_WORD);
+ lua_setfield(L, -2, "DBVT_WORD");
+ lua_pushnumber(L, DBVT_DWORD);
+ lua_setfield(L, -2, "DBVT_DWORD");
+ lua_pushnumber(L, DBVT_ASCIIZ);
+ lua_setfield(L, -2, "DBVT_ASCIIZ");
+ lua_pushnumber(L, DBVT_UTF8);
+ lua_setfield(L, -2, "DBVT_UTF8");
+ lua_pushnumber(L, DBVT_WCHAR);
+ lua_setfield(L, -2, "DBVT_WCHAR");
+
+ MT<DBCONTACTWRITESETTING>(L, MT_DBCONTACTWRITESETTING)
+ .Field(&DBCONTACTWRITESETTING::szModule, "Module", LUA_TSTRINGA)
+ .Field(&DBCONTACTWRITESETTING::szSetting, "Setting", LUA_TSTRINGA);
+
+ MT<DBEVENTINFO>(L, MT_DBEVENTINFO)
+ .Field(&DBEVENTINFO::szModule, "Module", LUA_TSTRINGA)
+ .Field(&DBEVENTINFO::timestamp, "Timestamp", LUA_TINTEGER)
+ .Field(&DBEVENTINFO::eventType, "Type", LUA_TINTEGER)
+ .Field(&DBEVENTINFO::flags, "Flags", LUA_TINTEGER);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_genmenu.cpp b/plugins/MirLua/src/Modules/m_genmenu.cpp
new file mode 100644
index 0000000000..0c2d0ab492
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_genmenu.cpp
@@ -0,0 +1,95 @@
+#include "../stdafx.h"
+
+static int genmenu_ModifyMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+ ptrW name(mir_utf8decodeW(lua_tostring(L, 2)));
+ HANDLE hIcolibItem = luaL_opt(L, lua_touserdata, 3, INVALID_HANDLE_VALUE);
+ int flags = luaL_optinteger(L, 4, -1);
+
+ if (!(flags & CMIF_UNICODE))
+ flags |= CMIF_UNICODE;
+
+ INT_PTR res = Menu_ModifyItem(hMenuItem, name, hIcolibItem, flags);
+ lua_pushboolean(L, res == 0);
+
+ return 1;
+}
+
+static int genmenu_ConfigureMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+ int option = luaL_checkinteger(L, 2);
+ luaL_checktype(L, 3, LUA_TLIGHTUSERDATA);
+ INT_PTR value = (INT_PTR)lua_touserdata(L, 3);
+
+ int res = Menu_ConfigureItem(hMenuItem, option, value);
+ lua_pushboolean(L, res >= 0);
+
+ return 1;
+}
+
+static int genmenu_ShowMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+ bool isShow = luaM_toboolean(L, 2);
+
+ Menu_ShowItem(hMenuItem, isShow);
+
+ return 0;
+}
+
+static int genmenu_EnableMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+ bool isEnable = luaM_toboolean(L, 2);
+
+ Menu_EnableItem(hMenuItem, isEnable);
+
+ return 0;
+}
+
+static int genmenu_CheckMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+ bool isChecked = luaM_toboolean(L, 2);
+
+ Menu_SetChecked(hMenuItem, isChecked);
+
+ return 0;
+}
+
+static int genmenu_RemoveMenuItem(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ HGENMENU hMenuItem = (HGENMENU)lua_touserdata(L, 1);
+
+ INT_PTR res = Menu_RemoveItem(hMenuItem);
+ lua_pushboolean(L, res == 0);
+
+ return 1;
+}
+
+static luaL_Reg genmenuApi[] =
+{
+ { "ModifyMenuItem", genmenu_ModifyMenuItem },
+ { "ConfigureMenuItem", genmenu_ConfigureMenuItem },
+ { "ShowMenuItem", genmenu_ShowMenuItem },
+ { "EnableMenuItem", genmenu_EnableMenuItem },
+ { "CheckMenuItem", genmenu_CheckMenuItem },
+ { "RemoveMenuItem", genmenu_RemoveMenuItem },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_genmenu(lua_State *L)
+{
+ luaL_newlib(L, genmenuApi);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_hotkeys.cpp b/plugins/MirLua/src/Modules/m_hotkeys.cpp
new file mode 100644
index 0000000000..1245d2348f
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_hotkeys.cpp
@@ -0,0 +1,103 @@
+#include "../stdafx.h"
+
+void MakeHotkey(lua_State *L, HOTKEYDESC &hk)
+{
+ lua_getfield(L, -1, "Flags");
+ hk.dwFlags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ if (!(hk.dwFlags & HKD_UNICODE))
+ hk.dwFlags |= HKD_UNICODE;
+
+ lua_getfield(L, -1, "Name");
+ hk.pszName = mir_utf8decodeA(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Description");
+ hk.szDescription.w = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Section");
+ hk.szSection.w = mir_utf8decodeW(luaL_optstring(L, -1, MODULENAME));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Hotkey");
+ hk.DefHotKey = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Service");
+ hk.pszService = mir_utf8decodeA(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "lParam");
+ hk.lParam = (LPARAM)lua_touserdata(L, -1);
+ lua_pop(L, 1);
+}
+
+static int hotkeys_Register(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ HOTKEYDESC hk;
+ MakeHotkey(L, hk);
+
+ int hScriptLangpack = CMLuaEnvironment::GetEnvironmentId(L);
+
+ INT_PTR res = Hotkey_Register(&hk, hScriptLangpack);
+ lua_pushboolean(L, res);
+
+ return 1;
+}
+
+static int hotkeys_Unregister(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+
+ Hotkey_Unregister(name);
+
+ return 0;
+}
+
+static const char *mods[] = { "shift", "ctrl", "alt", "ext", nullptr };
+
+static int hotkeys_MakeHotkey(lua_State *L)
+{
+ int mod = 0;
+ switch (lua_type(L, 1))
+ {
+ case LUA_TNUMBER:
+ mod = luaL_checkinteger(L, 1);
+ break;
+ case LUA_TSTRING:
+ mod = (1 << (luaL_checkoption(L, 1, nullptr, mods) - 1));
+ break;
+ case LUA_TTABLE:
+ for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1))
+ mod |= (1 << (luaL_checkoption(L, -1, nullptr, mods) - 1));
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+ int vk = luaL_checknumber(L, 2);
+
+ WORD res = HOTKEYCODE(mod, vk);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static luaL_Reg hotkeysApi[] =
+{
+ { "MakeHotkey", hotkeys_MakeHotkey },
+ { "Register", hotkeys_Register },
+ { "Unregister", hotkeys_Unregister },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_hotkeys(lua_State *L)
+{
+ luaL_newlib(L, hotkeysApi);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_http.cpp b/plugins/MirLua/src/Modules/m_http.cpp
new file mode 100644
index 0000000000..31def57a9a
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_http.cpp
@@ -0,0 +1,443 @@
+#include "../stdafx.h"
+
+/***********************************************/
+
+#define MT_NETLIBHTTPHEADERS "NETLIBHTTPHEADERS"
+
+struct NETLIBHTTPHEADERS
+{
+ const NETLIBHTTPHEADER *headers;
+ int count;
+};
+
+static void AddHeader(NETLIBHTTPREQUEST *request, const char *name, const char *value)
+{
+ request->headers = (NETLIBHTTPHEADER*)mir_realloc(request->headers,
+ sizeof(NETLIBHTTPHEADER)*(request->headersCount + 1));
+ NETLIBHTTPHEADER &header = request->headers[request->headersCount];
+ header.szName = mir_strdup(name);
+ header.szValue = mir_strdup(value);
+ request->headersCount++;
+}
+
+static void SetHeader(NETLIBHTTPREQUEST *request, const char *name, const char *value)
+{
+ for (int i = 0; i < request->headersCount; i++) {
+ if (mir_strcmp(request->headers[i].szName, name) == 0) {
+ mir_free(request->headers[i].szValue);
+ request->headers[i].szValue = mir_strdup(value);
+ return;
+ }
+ }
+ AddHeader(request, name, value);
+}
+
+static int headers_Iterator(lua_State *L)
+{
+ NETLIBHTTPHEADER *headers = (NETLIBHTTPHEADER*)lua_touserdata(L, lua_upvalueindex(1));
+ int count = lua_tointeger(L, lua_upvalueindex(2));
+ int idx = lua_tointeger(L, lua_upvalueindex(3));
+
+ if (idx < count) {
+ lua_pushstring(L, headers[idx].szName);
+ lua_pushstring(L, headers[idx].szValue);
+ lua_pushinteger(L, idx + 1);
+ lua_replace(L, lua_upvalueindex(3));
+ return 2;
+ }
+
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int headers__call(lua_State *L)
+{
+ NETLIBHTTPHEADERS *headers = (NETLIBHTTPHEADERS*)luaL_checkudata(L, 1, MT_NETLIBHTTPHEADERS);
+
+ lua_pushlightuserdata(L, (void*)headers->headers);
+ lua_pushinteger(L, headers->count);
+ lua_pushinteger(L, 0);
+ lua_pushcclosure(L, headers_Iterator, 3);
+
+ return 1;
+}
+
+static int headers__index(lua_State *L)
+{
+ NETLIBHTTPHEADERS *headers = (NETLIBHTTPHEADERS*)luaL_checkudata(L, 1, MT_NETLIBHTTPHEADERS);
+
+ if (lua_isinteger(L, 2)) {
+ int idx = lua_tointeger(L, 2);
+ if (idx > 0 && idx <= headers->count) {
+ lua_pushstring(L, headers->headers[idx - 1].szValue);
+ return 1;
+ }
+ }
+
+ const char *key = lua_tostring(L, 2);
+ for (int i = 0; i < headers->count; i++) {
+ if (mir_strcmp(headers->headers[i].szName, key) == 0) {
+ lua_pushstring(L, headers->headers[i].szValue);
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int headers__len(lua_State *L)
+{
+ NETLIBHTTPHEADERS *headers = (NETLIBHTTPHEADERS*)luaL_checkudata(L, 1, MT_NETLIBHTTPHEADERS);
+
+ lua_pushinteger(L, headers->count);
+
+ return 1;
+}
+
+static const luaL_Reg headersApi[] =
+{
+ { "__call", headers__call },
+ { "__index", headers__index },
+ { "__len", headers__len },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+#define MT_NETLIBHTTPCONTENT "NETLIBHTTPCONTENT"
+
+struct NETLIBHTTPCONTENT
+{
+ const char *data;
+ int length;
+};
+
+static int content__index(lua_State *L)
+{
+ NETLIBHTTPCONTENT *content = (NETLIBHTTPCONTENT*)luaL_checkudata(L, 1, MT_NETLIBHTTPCONTENT);
+
+ int idx = luaL_checkinteger(L, 2);
+ if (idx > 0 && idx <= content->length) {
+ lua_pushinteger(L, content->data[idx - 1]);
+ return 1;
+ }
+
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int content__len(lua_State *L)
+{
+ NETLIBHTTPCONTENT *content = (NETLIBHTTPCONTENT*)luaL_checkudata(L, 1, MT_NETLIBHTTPCONTENT);
+
+ lua_pushinteger(L, content->length);
+
+ return 1;
+}
+
+static int content__tostring(lua_State *L)
+{
+ NETLIBHTTPCONTENT *content = (NETLIBHTTPCONTENT*)luaL_checkudata(L, 1, MT_NETLIBHTTPCONTENT);
+
+ lua_pushlstring(L, content->data, content->length);
+
+ return 1;
+}
+
+static const luaL_Reg contentApi[] =
+{
+ { "__index", content__index },
+ { "__len", content__len },
+ { "__tostring", content__tostring },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+#define MT_NETLIBHTTPRESPONSE "NETLIBHTTPRESPONSE"
+
+static NETLIBHTTPREQUEST* response_Create(lua_State *L, NETLIBHTTPREQUEST *request)
+{
+ NETLIBHTTPREQUEST **response = (NETLIBHTTPREQUEST**)lua_newuserdata(L, sizeof(NETLIBHTTPREQUEST*));
+ *response = Netlib_HttpTransaction(hNetlib, request);
+ luaL_setmetatable(L, MT_NETLIBHTTPRESPONSE);
+
+ return *response;
+}
+
+static int response__index(lua_State *L)
+{
+ NETLIBHTTPREQUEST *response = *(NETLIBHTTPREQUEST**)luaL_checkudata(L, 1, MT_NETLIBHTTPRESPONSE);
+ const char *key = lua_tostring(L, 2);
+
+ if (mir_strcmpi(key, "Headers") == 0) {
+ NETLIBHTTPHEADERS *headers = (NETLIBHTTPHEADERS*)lua_newuserdata(L, sizeof(NETLIBHTTPHEADERS));
+ headers->headers = response->headers;
+ headers->count = response->headersCount;
+ luaL_setmetatable(L, MT_NETLIBHTTPHEADERS);
+ }
+ else if (mir_strcmpi(key, "Content") == 0) {
+ NETLIBHTTPCONTENT *content = (NETLIBHTTPCONTENT*)lua_newuserdata(L, sizeof(NETLIBHTTPCONTENT));
+ content->data = response->pData;
+ content->length = response->dataLength;
+ luaL_setmetatable(L, MT_NETLIBHTTPCONTENT);
+ }
+ else if (mir_strcmpi(key, "StatusCode") == 0)
+ lua_pushinteger(L, response->resultCode);
+ else if (mir_strcmpi(key, "IsSuccess") == 0) {
+ lua_pushboolean(L, HTTP_CODE_SUCCESS(response->resultCode));
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int response__gc(lua_State *L)
+{
+ NETLIBHTTPREQUEST **response = (NETLIBHTTPREQUEST**)luaL_checkudata(L, 1, MT_NETLIBHTTPRESPONSE);
+
+ Netlib_FreeHttpRequest(*response);
+
+ return 0;
+}
+
+static const luaL_Reg responseApi[] =
+{
+ { "__index", response__index },
+ { "__gc", response__gc },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+#define MT_NETLIBHTTPREQUEST "NETLIBHTTPREQUEST"
+
+static NETLIBHTTPREQUEST* CreateRequest(lua_State *L)
+{
+ NETLIBHTTPREQUEST **request = (NETLIBHTTPREQUEST**)lua_newuserdata(L, sizeof(NETLIBHTTPREQUEST*));
+ *request = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
+ (*request)->cbSize = sizeof(NETLIBHTTPREQUEST);
+ (*request)->flags = NLHRF_HTTP11 | NLHRF_NODUMP;
+
+ luaL_setmetatable(L, MT_NETLIBHTTPREQUEST);
+
+ return *request;
+}
+
+static void request_SetUrl(lua_State *L, int idx, NETLIBHTTPREQUEST *request)
+{
+ const char *url = luaL_checkstring(L, idx);
+ request->szUrl = mir_strdup(url);
+ if (mir_strncmpi(request->szUrl, "https", 5) == 0)
+ request->flags |= NLHRF_SSL;
+ else
+ request->flags &= ~(NLHRF_SSL);
+}
+
+static void request_SetHeaders(lua_State *L, int idx, NETLIBHTTPREQUEST *request)
+{
+ if (lua_isnoneornil(L, idx))
+ return;
+
+ luaL_checktype(L, idx, LUA_TTABLE);
+
+ idx = lua_absindex(L, idx);
+ for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 2)) {
+ lua_pushvalue(L, -2);
+ const char *name = lua_tostring(L, -1);
+ const char *value = lua_tostring(L, -2);
+ AddHeader(request, name, value);
+ }
+}
+
+static void request_SetContent(lua_State *L, int idx, NETLIBHTTPREQUEST *request)
+{
+ CMStringA data;
+
+ switch (lua_type(L, idx))
+ {
+ case LUA_TNONE:
+ case LUA_TNIL:
+ return;
+ case LUA_TSTRING:
+ data = lua_tostring(L, idx);
+ SetHeader(request, "Content-Type", "text/plain");
+ break;
+ case LUA_TTABLE:
+ {
+ idx = lua_absindex(L, idx);
+ for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 2)) {
+ lua_pushvalue(L, -2);
+ const char *name = lua_tostring(L, -1);
+ const char *value = lua_tostring(L, -2);
+ data.AppendFormat("&%s=%s", name, value);
+ }
+ data.Delete(0);
+ SetHeader(request, "Content-Type", "application/x-www-form-urlencoded");
+ break;
+ }
+ default:
+ luaL_argerror(L, idx, luaL_typename(L, idx));
+ }
+
+ request->pData = mir_strdup(data);
+ request->dataLength = data.GetLength();
+}
+
+static void request_SetContentType(lua_State *L, int idx, NETLIBHTTPREQUEST *request)
+{
+ if (!lua_isstring(L, idx))
+ return;
+
+ const char *type = lua_tostring(L, idx);
+ SetHeader(request, "Content-Type", type);
+}
+
+static const char *httpMethods[] = { "GET", "POST", "PUT", "DELETE", nullptr };
+
+static int request_Send(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ NETLIBHTTPREQUEST *request = CreateRequest(L);
+
+ lua_getfield(L, 1, "Method");
+ request->requestType = (1 << (luaL_checkoption(L, -1, nullptr, httpMethods)));
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "Url");
+ request_SetUrl(L, -1, request);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "Headers");
+ request_SetHeaders(L, -1, request);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "Content");
+ request_SetContent(L, -1, request);
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "Timeout");
+ request->timeout = luaL_optinteger(L, -1, 0);
+ lua_pop(L, 1);
+
+ response_Create(L, request);
+
+ return 1;
+}
+
+static int request_Get(lua_State *L)
+{
+ NETLIBHTTPREQUEST *request = CreateRequest(L);
+ request->requestType = REQUEST_GET;
+ request_SetUrl(L, 1, request);
+
+ response_Create(L, request);
+
+ return 1;
+}
+
+static int request_Post(lua_State *L)
+{
+ NETLIBHTTPREQUEST *request = CreateRequest(L);
+ request->requestType = REQUEST_POST;
+ request_SetUrl(L, 1, request);
+ request_SetContent(L, 2, request);
+ request_SetContentType(L, 3, request);
+
+ response_Create(L, request);
+
+ return 1;
+}
+
+static int request_Put(lua_State *L)
+{
+ NETLIBHTTPREQUEST *request = CreateRequest(L);
+ request->requestType = REQUEST_PUT;
+ request_SetUrl(L, 1, request);
+ request_SetContent(L, 2, request);
+ request_SetContentType(L, 3, request);
+
+ response_Create(L, request);
+
+ return 1;
+}
+
+static int request_Delete(lua_State *L)
+{
+ NETLIBHTTPREQUEST *request = CreateRequest(L);
+ request->requestType = REQUEST_DELETE;
+ request_SetUrl(L, 1, request);
+ request_SetContent(L, 2, request);
+ request_SetContentType(L, 2, request);
+
+ response_Create(L, request);
+
+ return 1;
+}
+
+static int request__gc(lua_State *L)
+{
+ NETLIBHTTPREQUEST **request = (NETLIBHTTPREQUEST**)luaL_checkudata(L, 1, MT_NETLIBHTTPREQUEST);
+
+ mir_free((*request)->szUrl);
+ for (int i = 0; i < (*request)->headersCount; i++) {
+ mir_free((*request)->headers[i].szName);
+ mir_free((*request)->headers[i].szValue);
+ }
+ mir_free((*request)->headers);
+ mir_free((*request)->pData);
+
+ return 0;
+}
+
+static const luaL_Reg requestApi[] =
+{
+ { "__gc", request__gc },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+static const luaL_Reg httpApi[] =
+{
+ { "Send", request_Send },
+ { "Get", request_Get },
+ { "Post", request_Post },
+ { "Put", request_Put },
+ { "Delete", request_Delete },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_http(lua_State *L)
+{
+ luaL_newlib(L, httpApi);
+
+ luaL_newmetatable(L, MT_NETLIBHTTPREQUEST);
+ luaL_setfuncs(L, requestApi, 0);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, MT_NETLIBHTTPRESPONSE);
+ luaL_setfuncs(L, responseApi, 0);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, MT_NETLIBHTTPHEADERS);
+ luaL_setfuncs(L, headersApi, 0);
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, MT_NETLIBHTTPCONTENT);
+ luaL_setfuncs(L, contentApi, 0);
+ lua_pop(L, 1);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_icolib.cpp b/plugins/MirLua/src/Modules/m_icolib.cpp
new file mode 100644
index 0000000000..86b5163290
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_icolib.cpp
@@ -0,0 +1,150 @@
+#include "../stdafx.h"
+
+static void MakeSKINICONDESC(lua_State *L, SKINICONDESC &sid)
+{
+ lua_getfield(L, -1, "Flags");
+ sid.flags = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ if (!(sid.flags & SIDF_ALL_UNICODE))
+ sid.flags |= SIDF_ALL_UNICODE;
+
+ lua_getfield(L, -1, "Name");
+ sid.pszName = mir_utf8decodeA(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Description");
+ sid.description.w = mir_utf8decodeW(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Section");
+ sid.section.w = mir_utf8decodeW(luaL_optstring(L, 3, MODULENAME));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "DefaultFile");
+ sid.defaultFile.w = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ if (sid.defaultFile.w == nullptr) {
+ sid.defaultFile.w = (wchar_t*)mir_calloc(MAX_PATH + 1);
+ GetModuleFileName(g_plugin.getInst(), sid.defaultFile.w, MAX_PATH);
+ }
+
+ lua_getfield(L, -1, "DefaultIndex");
+ sid.iDefaultIndex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "DefaultIcon");
+ sid.hDefaultIcon = (HICON)lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "SizeX");
+ sid.iDefaultIndex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "SizeY");
+ sid.iDefaultIndex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+}
+
+static int lua_AddIcon(lua_State *L)
+{
+ SKINICONDESC sid = { };
+
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ sid.flags = SIDF_ALL_UNICODE;
+ sid.pszName = mir_utf8decodeA(luaL_checkstring(L, 1));
+ sid.description.w = mir_utf8decodeW(luaL_checkstring(L, 2));
+ sid.section.w = mir_utf8decodeW(luaL_optstring(L, 3, MODULENAME));
+ sid.defaultFile.w = mir_utf8decodeW(lua_tostring(L, 4));
+ sid.hDefaultIcon = GetIcon(IDI_SCRIPT);
+
+ if (sid.defaultFile.w == nullptr) {
+ sid.defaultFile.w = (wchar_t*)mir_calloc(MAX_PATH + 1);
+ GetModuleFileName(g_plugin.getInst(), sid.defaultFile.w, MAX_PATH);
+ }
+ }
+ else if (lua_type(L, 1) == LUA_TTABLE)
+ MakeSKINICONDESC(L, sid);
+ else
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+
+ int hScriptLangpack = CMLuaEnvironment::GetEnvironmentId(L);
+ HANDLE res = IcoLib_AddIcon(&sid, hScriptLangpack);
+ lua_pushlightuserdata(L, res);
+
+ mir_free((void*)sid.pszName);
+ mir_free((void*)sid.description.w);
+ mir_free((void*)sid.section.w);
+ mir_free((void*)sid.defaultFile.w);
+
+ return 1;
+}
+
+static int lua_GetIcon(lua_State *L)
+{
+ bool big = luaM_toboolean(L, 2);
+
+ HICON hIcon = nullptr;
+ switch (lua_type(L, 1)) {
+ case LUA_TLIGHTUSERDATA:
+ hIcon = IcoLib_GetIconByHandle(lua_touserdata(L, 1), big);
+ break;
+ case LUA_TSTRING:
+ hIcon = IcoLib_GetIcon(lua_tostring(L, 1), big);
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ if (hIcon)
+ lua_pushlightuserdata(L, hIcon);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int lua_GetIconHandle(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+
+ HANDLE res = IcoLib_GetIconHandle(name);
+ lua_pushlightuserdata(L, res);
+
+ return 1;
+}
+
+static int lua_RemoveIcon(lua_State *L)
+{
+ switch (lua_type(L, 1)) {
+ case LUA_TLIGHTUSERDATA:
+ IcoLib_RemoveIconByHandle(lua_touserdata(L, 1));
+ break;
+ case LUA_TSTRING:
+ IcoLib_RemoveIcon(luaL_checkstring(L, 1));
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ return 0;
+}
+
+static luaL_Reg icolibApi[] =
+{
+ { "AddIcon", lua_AddIcon },
+ { "GetIcon", lua_GetIcon },
+ { "GetHandle", lua_GetIconHandle },
+ { "GetIconHandle", lua_GetIconHandle },
+ { "RemoveIcon", lua_RemoveIcon },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_icolib(lua_State *L)
+{
+ luaL_newlib(L, icolibApi);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_json.cpp b/plugins/MirLua/src/Modules/m_json.cpp
new file mode 100644
index 0000000000..24e32cab09
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_json.cpp
@@ -0,0 +1,243 @@
+#include "../stdafx.h"
+
+#define MT_JSON "JSON"
+
+static void lua2json(lua_State *L, JSONNode &node)
+{
+ switch (lua_type(L, -1)) {
+ case LUA_TNIL:
+ node.nullify();
+ break;
+ case LUA_TSTRING:
+ node = lua_tostring(L, -1);
+ break;
+ case LUA_TBOOLEAN:
+ node = lua_toboolean(L, -1) != 0;
+ break;
+ case LUA_TNUMBER:
+ {
+ lua_Integer val = lua_tointeger(L, -1);
+ if (lua_isinteger(L, -1) && val >= LONG_MIN && val <= LONG_MIN)
+ node = (long)val;
+ else
+ node = lua_tonumber(L, -1);
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ ptrA name(mir_strdup(node.name()));
+ node.cast(JSON_ARRAY);
+ node.set_name((char*)name);
+
+ lua_pushnil(L);
+ while (lua_next(L, -2) != 0) {
+ JSONNode child;
+ if (!lua_isnumber(L, -2)) {
+ if (node.type() == JSON_ARRAY) {
+ node.cast(JSON_NODE);
+ node.set_name((char*)name);
+ }
+ const char *key = lua_tostring(L, -2);
+ child.set_name(key);
+ }
+ lua2json(L, child);
+ node << child;
+
+ lua_pop(L, 1);
+ }
+
+ break;
+ }
+ }
+}
+
+/***********************************************/
+
+static int json__index(lua_State *L)
+{
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+
+ JSONNode *child;
+ if (node->type() == JSON_ARRAY) {
+ int idx = lua_tointeger(L, 2);
+ child = &node->at(idx - 1);
+ }
+ else {
+ const char *key = lua_tostring(L, 2);
+ child = &node->at(key);
+ }
+
+ switch (child->type())
+ {
+ case JSON_NULL:
+ lua_pushnil(L);
+ break;
+ case JSON_STRING:
+ lua_pushstring(L, child->as_string().c_str());
+ break;
+ case JSON_NUMBER:
+ lua_pushnumber(L, child->as_int());
+ break;
+ case JSON_BOOL:
+ lua_pushboolean(L, child->as_bool());
+ break;
+ case JSON_ARRAY:
+ case JSON_NODE:
+ JSONNode **udata = (JSONNode**)lua_newuserdata(L, sizeof(JSONNode*));
+ *udata = child;
+ luaL_setmetatable(L, MT_JSON);
+ }
+
+ return 1;
+}
+
+static int json__newindex(lua_State *L)
+{
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+ const char *key = lua_tostring(L, 2);
+
+ if (json_type(node) == JSON_ARRAY) {
+ int idx = lua_tointeger(L, 2);
+ JSONNode *child = json_at(node, idx - 1);
+ lua2json(L, *child);
+ return 0;
+ }
+
+ JSONNode *child = json_get(node, key);
+ if (json_type(child) == JSON_NULL) {
+ json_set_name(child, key);
+ lua2json(L, *child);
+ json_push_back(node, child);
+ return 0;
+ }
+
+ lua2json(L, *child);
+
+ return 0;
+}
+
+static int json__len(lua_State *L)
+{
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+ lua_pushnumber(L, json_size(node));
+
+ return 1;
+}
+
+static int json__tostring(lua_State *L)
+{
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+
+ lua_pushstring(L, node->write().c_str());
+
+ return 1;
+}
+
+static int json__gc(lua_State *L)
+{
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+
+ json_delete(node);
+
+ return 0;
+}
+
+const struct luaL_Reg jsonApi[] =
+{
+ { "__index", json__index },
+ { "__newindex", json__newindex },
+ { "__len", json__len },
+ { "__tostring", json__tostring },
+ { "__gc", json__gc },
+
+ { nullptr, nullptr }
+};
+
+
+/***********************************************/
+
+static int lua_Decode(lua_State *L)
+{
+ const char *string = luaL_checkstring(L, 1);
+
+ JSONNode **udata = (JSONNode**)lua_newuserdata(L, sizeof(JSONNode*));
+ *udata = json_parse(string);
+ luaL_setmetatable(L, MT_JSON);
+
+ return 1;
+}
+
+static int lua_Encode(lua_State *L)
+{
+ switch (lua_type(L, 1)) {
+ case LUA_TNIL:
+ lua_pushliteral(L, "null");
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, lua_toboolean(L, 1) ? "true" : "false");
+ break;
+ case LUA_TNUMBER:
+ {
+ if (lua_isinteger(L, 1)) {
+ lua_pushfstring(L, "%I", lua_tointeger(L, 1));
+ break;
+ }
+ char decpoint = lua_getlocaledecpoint();
+ if (decpoint != '.') {
+ char p[2] = { decpoint };
+ luaL_gsub(L, lua_tostring(L, 1), p, ".");
+ }
+ else
+ lua_pushfstring(L, "%f", lua_tonumber(L, 1));
+ break;
+ }
+ case LUA_TSTRING:
+ lua_pushfstring(L, "\"%s\"", lua_tostring(L, 1));
+ break;
+ case LUA_TTABLE:
+ {
+ JSONNode node;
+ lua_pushnil(L);
+ lua_pushvalue(L, 1);
+ lua2json(L, node);
+ lua_pop(L, 2);
+ lua_pushstring(L, node.write().c_str());
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ JSONNode *node = *(JSONNode**)luaL_checkudata(L, 1, MT_JSON);
+ lua_pushstring(L, node->write().c_str());
+ break;
+ }
+ case LUA_TLIGHTUSERDATA:
+ if (lua_touserdata(L, 1) == nullptr)
+ {
+ lua_pushliteral(L, "null");
+ break;
+ }
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ return 1;
+}
+
+static const luaL_Reg methods[] =
+{
+ { "Decode", lua_Decode },
+ { "Encode", lua_Encode },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_json(lua_State *L)
+{
+ luaL_newlib(L, methods);
+
+ luaL_newmetatable(L, MT_JSON);
+ luaL_setfuncs(L, jsonApi, 0);
+ lua_pop(L, 1);
+
+ return 1;
+} \ No newline at end of file
diff --git a/plugins/MirLua/src/Modules/m_message.cpp b/plugins/MirLua/src/Modules/m_message.cpp
new file mode 100644
index 0000000000..c369237796
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_message.cpp
@@ -0,0 +1,73 @@
+#include "../stdafx.h"
+
+static int message_Paste(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+ ptrW text(mir_utf8decodeW(luaL_checkstring(L, 2)));
+
+ MessageWindowData mwd;
+ INT_PTR res = Srmm_GetWindowData(hContact, mwd);
+ lua_pushinteger(L, res);
+ if (res)
+ return 1;
+
+ HWND hEdit = GetDlgItem(mwd.hwndWindow, 1002 /*IDC_MESSAGE*/);
+ if (!hEdit) hEdit = GetDlgItem(mwd.hwndWindow, 1009 /*IDC_CHATMESSAGE*/);
+
+ SendMessage(hEdit, EM_REPLACESEL, TRUE, (LPARAM)text);
+
+ return 1;
+}
+
+static int message_Send(lua_State *L)
+{
+ MCONTACT hContact = luaL_checkinteger(L, 1);
+ const char *message = luaL_checkstring(L, 2);
+
+ INT_PTR res = 1;
+
+ const char *szProto = GetContactProto(hContact);
+ if (db_get_b(hContact, szProto, "ChatRoom", 0) == TRUE) {
+ ptrW wszChatRoom(db_get_wsa(hContact, szProto, "ChatRoomID"));
+ ptrW wszMessage(mir_utf8decodeW(message));
+ res = Chat_SendUserMessage(szProto, wszChatRoom, wszMessage);
+ lua_pushinteger(L, res);
+ }
+ else if ((res = ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)message)) != ACKRESULT_FAILED) {
+ DBEVENTINFO dbei = {};
+ dbei.szModule = MODULENAME;
+ dbei.timestamp = time(0);
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = (DWORD)mir_strlen(message);
+ dbei.pBlob = (PBYTE)mir_strdup(message);
+ dbei.flags = DBEF_UTF | DBEF_SENT;
+ db_event_add(hContact, &dbei);
+
+ lua_pushinteger(L, res);
+ return 1;
+ }
+
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static luaL_Reg messageApi[] =
+{
+ { "Paste", message_Paste },
+ { "Send", message_Send },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_message(lua_State *L)
+{
+ luaL_newlib(L, messageApi);
+
+ MT<MessageWindowEventData>(L, "MessageWindowEventData")
+ .Field(&MessageWindowEventData::uType, "Type", LUA_TINTEGER)
+ .Field(&MessageWindowEventData::hContact, "hContact", LUA_TINTEGER)
+ .Field(&MessageWindowEventData::uFlags, "Flags", LUA_TINTEGER);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_options.cpp b/plugins/MirLua/src/Modules/m_options.cpp
new file mode 100644
index 0000000000..babcfe3909
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_options.cpp
@@ -0,0 +1,117 @@
+#include "../stdafx.h"
+
+class CMLuaScriptOptionPage : public CDlgBase
+{
+private:
+ int m_onInitDialogRef;
+ int m_onApplyRef;
+ lua_State *L;
+
+public:
+ CMLuaScriptOptionPage(lua_State *_L, int onInitDialogRef, int onApplyRef)
+ : CDlgBase(g_plugin, IDD_SCRIPTOPTIONSPAGE), L(_L),
+ m_onInitDialogRef(onInitDialogRef), m_onApplyRef(onApplyRef)
+ {
+ }
+
+ void OnInitDialog() override
+ {
+ if (m_onInitDialogRef)
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, m_onInitDialogRef);
+ lua_pushlightuserdata(L, m_hwnd);
+ luaM_pcall(L, 1, 0);
+ }
+ }
+
+ void OnApply() override
+ {
+ if (m_onApplyRef)
+ {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, m_onApplyRef);
+ lua_pushlightuserdata(L, m_hwnd);
+ luaM_pcall(L, 1, 0);
+ }
+ }
+
+ void OnDestroy() override
+ {
+ lua_pushnil(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, this);
+ }
+};
+
+void MakeOptionDialogPage(lua_State *L, OPTIONSDIALOGPAGE &odp)
+{
+ odp.hInstance = g_plugin.getInst();
+ odp.langId = CMLuaEnvironment::GetEnvironmentId(L);
+
+ lua_getfield(L, -1, "Flags");
+ odp.flags = luaL_optinteger(L, -1, ODPF_BOLDGROUPS | ODPF_UNICODE | ODPF_DONTTRANSLATE);
+ lua_pop(L, 1);
+
+ if (!(odp.flags & ODPF_UNICODE))
+ odp.flags |= ODPF_UNICODE;
+
+ lua_getfield(L, -1, "Group");
+ odp.szGroup.w = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Title");
+ odp.szTitle.w = mir_utf8decodeW(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Tab");
+ odp.szTab.w = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ int onInitDialogRef = LUA_NOREF;
+ lua_getfield(L, -1, "OnInitDialog");
+ if (lua_isfunction(L, -1))
+ onInitDialogRef = luaL_ref(L, LUA_REGISTRYINDEX);
+ else
+ lua_pop(L, 1);
+
+ int onApplyRef = LUA_NOREF;
+ lua_getfield(L, -1, "OnApply");
+ if (lua_isfunction(L, -1))
+ onApplyRef = luaL_ref(L, LUA_REGISTRYINDEX);
+ else
+ lua_pop(L, 1);
+
+ lua_State *T = lua_newthread(L);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, T);
+ odp.pDialog = new CMLuaScriptOptionPage(T, onInitDialogRef, onApplyRef);
+}
+
+int opt_AddPage(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
+ WPARAM wParam = (WPARAM)lua_touserdata(L, 1);
+
+ OPTIONSDIALOGPAGE odp = { 0 };
+ MakeOptionDialogPage(L, odp);
+
+ INT_PTR res = Options_AddPage(wParam, &odp);
+ lua_pushboolean(L, !res);
+
+ mir_free(odp.szGroup.w);
+ mir_free(odp.szTitle.w);
+ mir_free(odp.szTab.w);
+
+ return 1;
+}
+
+static luaL_Reg optionsApi[] =
+{
+ { "AddPage", opt_AddPage },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_options(lua_State *L)
+{
+ luaL_newlib(L, optionsApi);
+
+ return 1;
+} \ No newline at end of file
diff --git a/plugins/MirLua/src/Modules/m_protocols.cpp b/plugins/MirLua/src/Modules/m_protocols.cpp
new file mode 100644
index 0000000000..1dac3e3ea6
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_protocols.cpp
@@ -0,0 +1,281 @@
+#include "../stdafx.h"
+
+#define MT_PROTOCOLDESCRIPTOR "PROTOCOLDESCRIPTOR"
+
+HANDLE hRecvMessage = nullptr;
+
+static int lua_GetProtocol(lua_State *L)
+{
+ const char *szProto = nullptr;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TNUMBER:
+ {
+ const char *szModule = GetContactProto(lua_tonumber(L, 1));
+ PROTOACCOUNT *pa = Proto_GetAccount(szModule);
+ if (pa)
+ szProto = pa->szProtoName;
+ break;
+ }
+ case LUA_TSTRING:
+ szProto = lua_tostring(L, 1);
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ PROTOCOLDESCRIPTOR *pd = Proto_IsProtocolLoaded(szProto);
+ if (pd)
+ MT<PROTOCOLDESCRIPTOR>::Apply(L, pd);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int lua_ProtocolIterator(lua_State *L)
+{
+ int i = lua_tointeger(L, lua_upvalueindex(1));
+ int count = lua_tointeger(L, lua_upvalueindex(2));
+ PROTOCOLDESCRIPTOR **protos = (PROTOCOLDESCRIPTOR**)lua_touserdata(L, lua_upvalueindex(3));
+
+ if (i < count) {
+ lua_pushinteger(L, (i + 1));
+ lua_replace(L, lua_upvalueindex(1));
+ MT<PROTOCOLDESCRIPTOR>::Apply(L, protos[i]);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int lua_Protocols(lua_State *L)
+{
+ int count;
+ PROTOCOLDESCRIPTOR **protos;
+ Proto_EnumProtocols(&count, &protos);
+
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, count);
+ lua_pushlightuserdata(L, protos);
+ lua_pushcclosure(L, lua_ProtocolIterator, 3);
+
+ return 1;
+}
+
+static int lua_ChainSend(lua_State *L)
+{
+ MCONTACT hContact = luaL_checknumber(L, 1);
+ const char *service = luaL_checkstring(L, 2);
+ WPARAM wParam = (WPARAM)luaM_tomparam(L, 3);
+ LPARAM lParam = (LPARAM)luaM_tomparam(L, 4);
+
+ INT_PTR res = ProtoChainSend(hContact, service, wParam, lParam);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+static int lua_ChainRecv(lua_State *L)
+{
+ MCONTACT hContact = luaL_checknumber(L, 1);
+ const char *service = luaL_checkstring(L, 2);
+ WPARAM wParam = (WPARAM)luaM_tomparam(L, 3);
+ LPARAM lParam = (LPARAM)luaM_tomparam(L, 4);
+
+ INT_PTR res = ProtoChainRecv(hContact, service, wParam, lParam);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+/***********************************************/
+
+static int lua_GetAccount(lua_State *L)
+{
+ const char *name = nullptr;
+
+ switch (lua_type(L, 1))
+ {
+ case LUA_TNUMBER:
+ name = GetContactProto(lua_tonumber(L, 1));
+ break;
+ case LUA_TSTRING:
+ name = lua_tostring(L, 1);
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ PROTOACCOUNT *pa = Proto_GetAccount(name);
+ if (pa)
+ MT<PROTOACCOUNT>::Apply(L, pa);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int lua_AccountIterator(lua_State *L)
+{
+ int i = lua_tointeger(L, lua_upvalueindex(1));
+ int count = lua_tointeger(L, lua_upvalueindex(2));
+ PROTOACCOUNT **accounts = (PROTOACCOUNT**)lua_touserdata(L, lua_upvalueindex(3));
+ const char *szProto = lua_tostring(L, lua_upvalueindex(4));
+
+ if (szProto)
+ while (i < count && mir_strcmp(szProto, accounts[i]->szProtoName))
+ i++;
+
+ if (i < count)
+ {
+ lua_pushinteger(L, (i + 1));
+ lua_replace(L, lua_upvalueindex(1));
+ MT<PROTOACCOUNT>::Apply(L, accounts[i]);
+ }
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+static int lua_Accounts(lua_State *L)
+{
+ const char *szProto = nullptr;
+
+ switch (lua_type(L, 1))
+ {
+ case LUA_TNONE:
+ break;
+ case LUA_TSTRING:
+ szProto = lua_tostring(L, 1);
+ break;
+ case LUA_TUSERDATA:
+ {
+ PROTOCOLDESCRIPTOR *pd = *(PROTOCOLDESCRIPTOR**)luaL_checkudata(L, 1, MT_PROTOCOLDESCRIPTOR);
+ szProto = pd->szName;
+ break;
+ }
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ int count;
+ PROTOACCOUNT **accounts;
+ Proto_EnumAccounts(&count, &accounts);
+
+ lua_pushinteger(L, 0);
+ lua_pushinteger(L, count);
+ lua_pushlightuserdata(L, accounts);
+ lua_pushstring(L, szProto);
+ lua_pushcclosure(L, lua_AccountIterator, 4);
+
+ return 1;
+}
+
+static int lua_CallService(lua_State *L)
+{
+ const char *szModule = nullptr;
+
+ switch (lua_type(L, 1))
+ {
+ case LUA_TNUMBER:
+ szModule = GetContactProto(lua_tonumber(L, 1));
+ break;
+ case LUA_TSTRING:
+ szModule = lua_tostring(L, 1);
+ break;
+ case LUA_TUSERDATA:
+ {
+ PROTOACCOUNT *pa = *(PROTOACCOUNT**)luaL_checkudata(L, 1, MT_PROTOACCOUNT);
+ szModule = pa->szModuleName;
+ break;
+ }
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ const char *service = luaL_checkstring(L, 2);
+ WPARAM wParam = (WPARAM)luaM_tomparam(L, 3);
+ LPARAM lParam = (LPARAM)luaM_tomparam(L, 4);
+
+ INT_PTR res = CallProtoService(szModule, service, wParam, lParam);
+ lua_pushinteger(L, res);
+
+ return 1;
+}
+
+/***********************************************/
+
+INT_PTR FilterRecvMessage(WPARAM wParam, LPARAM lParam)
+{
+ int res = NotifyEventHooks(hRecvMessage, wParam, lParam);
+ if (res) return res;
+ Proto_ChainRecv(wParam, (CCSDATA*)lParam);
+ return 0;
+}
+
+/***********************************************/
+
+static luaL_Reg protocolsApi[] =
+{
+ { "GetProtocol", lua_GetProtocol },
+ { "Protocols", lua_Protocols },
+
+ { "CallSendChain", lua_ChainSend },
+ { "CallReceiveChain", lua_ChainRecv },
+
+ { "GetAccount", lua_GetAccount },
+ { "Accounts", lua_Accounts },
+
+ { "CallService", lua_CallService },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+LUAMOD_API int luaopen_m_protocols(lua_State *L)
+{
+ hRecvMessage = CreateHookableEvent(MODULENAME PSR_MESSAGE);
+ CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, FilterRecvMessage);
+
+ luaL_newlib(L, protocolsApi);
+
+ MT<PROTOCOLDESCRIPTOR>(L, MT_PROTOCOLDESCRIPTOR)
+ .Field(&PROTOCOLDESCRIPTOR::szName, "Name", LUA_TSTRINGA)
+ .Field(&PROTOCOLDESCRIPTOR::type, "Type", LUA_TINTEGER)
+ .Field(lua_Accounts, "Accounts");
+
+ MT<PROTOACCOUNT>(L, MT_PROTOACCOUNT)
+ .Field(&PROTOACCOUNT::szModuleName, "ModuleName", LUA_TSTRINGA)
+ .Field(&PROTOACCOUNT::tszAccountName, "AccountName", LUA_TSTRINGW)
+ .Field(&PROTOACCOUNT::szProtoName, "ProtoName", LUA_TSTRINGA)
+ .Field(&PROTOACCOUNT::bIsEnabled, "IsEnabled", LUA_TBOOLEAN)
+ .Field(&PROTOACCOUNT::bIsVisible, "IsVisible", LUA_TBOOLEAN)
+ .Field(&PROTOACCOUNT::bIsVirtual, "IsVirtual", LUA_TBOOLEAN)
+ .Field(&PROTOACCOUNT::bOldProto, "IsOldProto", LUA_TBOOLEAN)
+ .Field(lua_CallService, "CallService");
+
+ MT<ACKDATA>(L, "ACKDATA")
+ .Field(&ACKDATA::szModule, "Module", LUA_TSTRINGA)
+ .Field(&ACKDATA::hContact, "hContact", LUA_TINTEGER)
+ .Field(&ACKDATA::type, "Type", LUA_TINTEGER)
+ .Field(&ACKDATA::result, "Result", LUA_TINTEGER)
+ .Field(&ACKDATA::hProcess, "hProcess", LUA_TLIGHTUSERDATA)
+ .Field(&ACKDATA::lParam, "lParam", LUA_TLIGHTUSERDATA);
+
+ MT<CCSDATA>(L, "CCSDATA")
+ .Field(&CCSDATA::hContact, "hContact", LUA_TINTEGER)
+ .Field(&CCSDATA::szProtoService, "Service", LUA_TSTRINGA)
+ .Field(&CCSDATA::wParam, "wParam", LUA_TLIGHTUSERDATA)
+ .Field(&CCSDATA::lParam, "lParam", LUA_TLIGHTUSERDATA);
+
+ MT<PROTORECVEVENT>(L, "PROTORECVEVENT")
+ .Field(&PROTORECVEVENT::timestamp, "Timestamp", LUA_TINTEGER)
+ .Field(&PROTORECVEVENT::flags, "Flags", LUA_TINTEGER)
+ .Field(&PROTORECVEVENT::szMessage, "Message", LUA_TSTRING);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_sounds.cpp b/plugins/MirLua/src/Modules/m_sounds.cpp
new file mode 100644
index 0000000000..74b17f12cd
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_sounds.cpp
@@ -0,0 +1,54 @@
+#include "../stdafx.h"
+
+static int lua_AddSound(lua_State *L)
+{
+ ptrA name(mir_utf8decodeA(luaL_checkstring(L, 1)));
+ ptrW description(mir_utf8decodeW(luaL_checkstring(L, 2)));
+ ptrW section(mir_utf8decodeW(luaL_optstring(L, 3, MODULENAME)));
+ ptrW filePath(mir_utf8decodeW(lua_tostring(L, 4)));
+
+ int res = 1;
+ CMPluginBase *pPlugin = CMLuaEnvironment::GetEnvironment(L);
+ if (pPlugin != nullptr)
+ res = pPlugin->addSound(name, section, description, filePath);
+ lua_pushboolean(L, res == 0);
+
+ return 1;
+}
+
+static int lua_PlaySound(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+
+ INT_PTR res = Skin_PlaySound(name);
+ lua_pushboolean(L, res == 0);
+
+ return 1;
+}
+
+static int lua_PlayFile(lua_State *L)
+{
+ ptrW filePath(mir_utf8decodeW(luaL_checkstring(L, 1)));
+
+ INT_PTR res = Skin_PlaySoundFile(filePath);
+ lua_pushboolean(L, res == 0);
+
+ return 1;
+}
+
+static luaL_Reg soundApi[] =
+{
+ { "AddSound", lua_AddSound },
+
+ { "PlaySound", lua_PlaySound },
+ { "PlayFile", lua_PlayFile },
+
+ { nullptr, nullptr }
+};
+
+LUAMOD_API int luaopen_m_sounds(lua_State *L)
+{
+ luaL_newlib(L, soundApi);
+
+ return 1;
+}
diff --git a/plugins/MirLua/src/Modules/m_srmm.cpp b/plugins/MirLua/src/Modules/m_srmm.cpp
new file mode 100644
index 0000000000..f7296d1d70
--- /dev/null
+++ b/plugins/MirLua/src/Modules/m_srmm.cpp
@@ -0,0 +1,138 @@
+#include "../stdafx.h"
+
+#define MT_BBBUTTON "BBButton"
+
+static void MakeBBButton(lua_State *L, BBButton &bbb)
+{
+ bbb.dwDefPos = 100;
+
+ lua_getfield(L, -1, "Module");
+ bbb.pszModuleName = mir_utf8decodeA(luaL_checkstring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "ButtonId");
+ bbb.dwButtonID = luaL_checkinteger(L, -1);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Flags");
+ bbb.bbbFlags = luaL_optinteger(L, -1, BBBF_ISIMBUTTON);
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Text");
+ bbb.pwszText = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Tooltip");
+ bbb.pwszTooltip = mir_utf8decodeW(lua_tostring(L, -1));
+ lua_pop(L, 1);
+
+ lua_getfield(L, -1, "Icon");
+ bbb.hIcon = (HANDLE)lua_touserdata(L, -1);
+ lua_pop(L, 1);
+}
+
+static void CleanBBButton(BBButton &bbb)
+{
+ mir_free((void*)bbb.pszModuleName);
+ mir_free((void*)bbb.pwszText);
+ mir_free((void*)bbb.pwszTooltip);
+}
+
+/***********************************************/
+
+static int lua_AddButton(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ BBButton bbb = {};
+ MakeBBButton(L, bbb);
+
+ int hScriptLangpack = CMLuaEnvironment::GetEnvironmentId(L);
+ HANDLE res = Srmm_AddButton(&bbb, hScriptLangpack);
+ CleanBBButton(bbb);
+
+ if (!res)
+ {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_pushvalue(L, 1);
+ luaL_setmetatable(L, MT_BBBUTTON);
+
+ return 1;
+}
+
+static int lua_ModifyButton(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TTABLE);
+
+ lua_pushvalue(L, 1);
+
+ BBButton bbb = {};
+ MakeBBButton(L, bbb);
+ INT_PTR res = Srmm_ModifyButton(&bbb);
+ CleanBBButton(bbb);
+ lua_pushboolean(L, !res);
+
+ return 2;
+}
+
+static int lua_RemoveButton(lua_State *L)
+{
+ BBButton bbb = {};
+
+ switch (lua_type(L, 1))
+ {
+ case LUA_TSTRING:
+ bbb.pszModuleName = mir_utf8decodeA(lua_tostring(L, 1));
+ bbb.dwButtonID = luaL_checkinteger(L, 2);
+ break;
+ case LUA_TTABLE:
+ MakeBBButton(L, bbb);
+ break;
+ default:
+ luaL_argerror(L, 1, luaL_typename(L, 1));
+ }
+
+ INT_PTR res = Srmm_RemoveButton(&bbb);
+ CleanBBButton(bbb);
+ lua_pushboolean(L, !res);
+
+ return 1;
+}
+
+static luaL_Reg srmmApi[] =
+{
+ { "AddButton", lua_AddButton },
+ { "ModifyButton", lua_ModifyButton },
+ { "RemoveButton", lua_RemoveButton },
+
+ { nullptr, nullptr }
+};
+
+/***********************************************/
+
+LUAMOD_API int luaopen_m_srmm(lua_State *L)
+{
+ luaL_newlib(L, srmmApi);
+
+ luaL_newmetatable(L, MT_BBBUTTON);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ lua_pushliteral(L, MT_BBBUTTON);
+ lua_setfield(L, -2, "__metatable");
+ lua_pushcfunction(L, lua_ModifyButton);
+ lua_setfield(L, -2, "Modify");
+ lua_pushcfunction(L, lua_RemoveButton);
+ lua_setfield(L, -2, "Remove");
+ lua_pop(L, 1);
+
+ MT<CustomButtonClickData>(L, "CustomButtonClickData")
+ .Field(&CustomButtonClickData::pszModule, "Module", LUA_TSTRINGA)
+ .Field(&CustomButtonClickData::dwButtonId, "ButtonId", LUA_TINTEGER)
+ .Field(&CustomButtonClickData::hContact, "hContact", LUA_TINTEGER)
+ .Field(&CustomButtonClickData::flags, "Flags", LUA_TINTEGER);
+
+ return 1;
+}