commit ca88374fbdea0bc6aa8cbaa0906cab74a588ca91 Author: NatureFreshMilk Date: Mon Sep 16 08:06:54 2019 +0200 separated from webmail mod diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..ec6a1d2 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,23 @@ +unused_args = false +allow_defined_top = true + +globals = { + "mail", +} + +read_globals = { + -- Stdlib + string = {fields = {"split"}}, + table = {fields = {"copy", "getn"}}, + + -- Minetest + "minetest", + "vector", "ItemStack", + "dump", + + -- Deps + "unified_inventory", "default", + + -- optional mods + "xban" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..caa7028 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + +The file textures/mail_button.png was created by bas080 and is licensed under the WTFPL. + +Webmail component: +WTFPL + +All other files: + +Copyright (c) 2016 Carter Kolwey ("Cheapie Systems") +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and/or any associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..37ce2b0 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +Mail mod for Minetest +====== + +This is a fork of cheapies mail mod + +It adds a mail-system that allows players to send each other messages in-game and via webmail (optional) + +# Screenshots + +Ingame mail +![](pics/ingame.png?raw=true) + +# Installation + +## In-game mail mod + +Install it like any other mod: copy the directory `mail_mod` to your "worldmods" folder + +## Webmail + +See: https://github.com/thomasrudin-mt/mail + +# Commands/Howto + +To access your mail click on the inventory mail button or use the "/mail" command +Mails can be deleted, marked as read or unread, replied to and forwarded to another player + +# Dependencies +* None + + +# License + +See the "LICENSE" file + +# Old/Historic stuff +* Old forum topic: https://forum.minetest.net/viewtopic.php?t=14464 +* Old mod: https://cheapiesystems.com/git/mail/ diff --git a/api.lua b/api.lua new file mode 100644 index 0000000..1156ea3 --- /dev/null +++ b/api.lua @@ -0,0 +1,67 @@ +-- see: mail.md + +mail.registered_on_receives = {} +function mail.register_on_receive(func) + mail.registered_on_receives[#mail.registered_on_receives + 1] = func +end + +mail.receive_mail_message = "You have a new message from %s! Subject: %s\nTo view it, type /mail" +mail.read_later_message = "You can read your messages later by using the /mail command" + +--[[ +mail sending function, can be invoked with one object argument (new api) or +all 4 parameters (old compat version) +see: "Mail format" api.md +--]] +function mail.send(sender, receiver, subject, body) + local m + if receiver == nil and subject == nil and body == nil then + -- new format (one object param) + m = sender + + else + -- old format + -- create mail from params + + m = {} + m.sender = sender + m.receiver = receiver + m.subject = subject + m.body = body + + end + + m.unread = true + + if not m.time then + -- add timestamp + m.time = os.time() + end + + + minetest.log("action", "[mail] '" .. m.sender .. "' sends mail to '" .. m.receiver .. + "' with subject '" .. m.subject .. "' and body: '" .. m.body .. "'") + + local messages = mail.getMessages(m.receiver) + + table.insert(messages, 1, m) + mail.setMessages(m.receiver, messages) + + for _, player in ipairs(minetest.get_connected_players()) do + local name = player:get_player_name() + if name == m.receiver then + if m.subject == "" then m.subject = "(No subject)" end + if string.len(m.subject) > 30 then + m.subject = string.sub(m.subject,1,27) .. "..." + end + minetest.chat_send_player(m.receiver, + string.format(mail.receive_mail_message, m.sender, m.subject)) + end + end + + for i=1, #mail.registered_on_receives do + if mail.registered_on_receives[i](m) then + break + end + end +end diff --git a/api.md b/api.md new file mode 100644 index 0000000..dec0a70 --- /dev/null +++ b/api.md @@ -0,0 +1,60 @@ + +# Mail format +The mail format in the api hooks + +```lua +mail = { + sender = "source name", + receiver = "destination name", + subject = "subject line", + body = "mail body", + -- 8 attachments max + attachments = {"default:stone 99", "default:gold_ingot 99"} +} +``` + +## Sending mail +Old variant (pre-1.1) +```lua +mail.send("source name", "destination name", "subject line", "mail body") +``` + +New variant (1.1+) +```lua +mail.send({ + sender = "source name", + receiver = "destination name", + subject = "subject line", + body = "mail body" +}) +``` + +# Hooks +On-receive mail hook: + +```lua +mail.register_on_receive(function(m) + -- "m" is an object in the form: "Mail format" +end) +``` + +# internal mail format (on-disk) +The mail format on-disk + +> (worldfolder)/mails/(playername).json + +```json +[{ + "unread": true, + "sender": "sender name", + "receiver": "receiver name", + "subject": "subject name", + "body": "main\nmultiline\nbody", + "time": 1551258349, + "attachments": [ + "default:stone 99", + "default:gold_ingot 99" + ] +}] + +``` diff --git a/attachment.lua b/attachment.lua new file mode 100644 index 0000000..efb76d9 --- /dev/null +++ b/attachment.lua @@ -0,0 +1,27 @@ + +local invmap = {} + + +mail.getAttachmentInventory = function(playername) + return invmap[playername] +end + +mail.getAttachmentInventoryName = function(playername) + return "mail:" .. playername +end + + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + local inv = minetest.create_detached_inventory(mail.getAttachmentInventoryName(name), {}) + + invmap[name] = inv +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + invmap[name] = nil + if minetest.remove_detached_inventory then + minetest.remove_detached_inventory(mail.getAttachmentInventoryName(name)) + end +end) diff --git a/chatcommands.lua b/chatcommands.lua new file mode 100644 index 0000000..044d246 --- /dev/null +++ b/chatcommands.lua @@ -0,0 +1,6 @@ +minetest.register_chatcommand("mail",{ + description = "Open the mail interface", + func = function(name) + mail.show_inbox(name) + end +}) diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..709b3a7 --- /dev/null +++ b/depends.txt @@ -0,0 +1,3 @@ +unified_inventory? +default? +xban2? \ No newline at end of file diff --git a/gui.lua b/gui.lua new file mode 100644 index 0000000..e165517 --- /dev/null +++ b/gui.lua @@ -0,0 +1,250 @@ +selected_message_idxs = {} + +local theme +if minetest.get_modpath("default") then + theme = default.gui_bg .. default.gui_bg_img +else + theme = "" +end + +mail.inbox_formspec = "size[8,9;]" .. theme .. [[ + button_exit[7.5,0;0.5,0.5;quit;X] + button[6,1;2,0.5;new;New Message] + button[6,2;2,0.5;read;Read] + button[6,3;2,0.5;reply;Reply] + button[6,4;2,0.5;forward;Forward] + button[6,5;2,0.5;delete;Delete] + button[6,6;2,0.5;markread;Mark Read] + button[6,7;2,0.5;markunread;Mark Unread] + button[6,8;2,0.5;about;About] + tablecolumns[color;text;text] + table[0,0;5.75,9;messages;#999,From,Subject]] + + +function mail.show_about(name) + local formspec = [[ + size[8,5;] + button[7.5,0;0.5,0.5;back;X] + label[0,0;Mail] + label[0,0.5;By cheapie] + label[0,1;http://github.com/cheapie/mail] + label[0,1.5;See LICENSE file for license information] + label[0,2.5;NOTE: Communication using this system] + label[0,3;is NOT guaranteed to be private!] + label[0,3.5;Admins are able to view the messages] + label[0,4;of any player.] + ]] .. theme + + minetest.show_formspec(name, "mail:about", formspec) +end + +function mail.show_inbox(name) + local formspec = { mail.inbox_formspec } + local messages = mail.getMessages(name) + + if messages[1] then + for idx, message in ipairs(messages) do + if message.unread then + formspec[#formspec + 1] = ",#FFD700" + else + formspec[#formspec + 1] = "," + end + formspec[#formspec + 1] = "," + formspec[#formspec + 1] = minetest.formspec_escape(message.sender) + formspec[#formspec + 1] = "," + if message.subject ~= "" then + if string.len(message.subject) > 30 then + formspec[#formspec + 1] = + minetest.formspec_escape(string.sub(message.subject, 1, 27)) + formspec[#formspec + 1] = "..." + else + formspec[#formspec + 1] = minetest.formspec_escape(message.subject) + end + else + formspec[#formspec + 1] = "(No subject)" + end + end + if selected_message_idxs[name] then + formspec[#formspec + 1] = ";" + formspec[#formspec + 1] = tostring(selected_message_idxs[name] + 1) + end + formspec[#formspec + 1] = "]" + else + formspec[#formspec + 1] = "]label[2,4.5;No mail]" + end + minetest.show_formspec(name, "mail:inbox", table.concat(formspec, "")) +end + +function mail.show_message(name, msgnumber) + local messages = mail.getMessages(name) + local message = messages[msgnumber] + local formspec = [[ + size[8,7.2] + button[7,0;1,0.5;back;X] + label[0,0;From: %s] + label[0,0.5;Subject: %s] + textarea[0.25,1.25;8,6.25;body;;%s] + button[1,6.7;2,1;reply;Reply] + button[3,6.7;2,1;forward;Forward] + button[5,6.7;2,1;delete;Delete] + ]] .. theme + + local sender = minetest.formspec_escape(message.sender) + local subject = minetest.formspec_escape(message.subject) + local body = minetest.formspec_escape(message.body) + formspec = string.format(formspec, sender, subject, body) + + minetest.show_formspec(name,"mail:message",formspec) +end + +function mail.show_compose(name, defaulttgt, defaultsubj, defaultbody) + local formspec = [[ + size[8,7.2] + field[0.25,0.5;4,1;to;To:;%s] + field[0.25,1.7;8,1;subject;Subject:;%s] + textarea[0.25,2.4;8,5;body;;%s] + button[0.5,6.7;3,1;cancel;Cancel] + button[7,0;1,0.5;cancel;X] + button[4.5,6.7;3,1;send;Send] + ]] .. theme + + formspec = string.format(formspec, + minetest.formspec_escape(defaulttgt), + minetest.formspec_escape(defaultsubj), + minetest.formspec_escape(defaultbody)) + + minetest.show_formspec(name, "mail:compose", formspec) +end + +function mail.handle_receivefields(player, formname, fields) + if formname == "" and fields and fields.quit and minetest.get_modpath("unified_inventory") then + unified_inventory.set_inventory_formspec(player, "craft") + end + + if formname == "mail:about" then + minetest.after(0.5, function() + mail.show_inbox(player:get_player_name()) + end) + + elseif formname == "mail:inbox" then + local name = player:get_player_name() + local messages = mail.getMessages(name) + + if fields.messages then + local evt = minetest.explode_table_event(fields.messages) + selected_message_idxs[name] = evt.row - 1 + if evt.type == "DCL" and messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + mail.show_message(name, selected_message_idxs[name]) + end + mail.setMessages(name, messages) + return true + end + if fields.read then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + mail.show_message(name, selected_message_idxs[name]) + end + + elseif fields.delete then + if messages[selected_message_idxs[name]] then + table.remove(messages, selected_message_idxs[name]) + end + + mail.show_inbox(name) + elseif fields.reply and messages[selected_message_idxs[name]] then + local message = messages[selected_message_idxs[name]] + local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, message.sender, "Re: "..message.subject,replyfooter) + + elseif fields.forward and messages[selected_message_idxs[name]] then + local message = messages[selected_message_idxs[name]] + local fwfooter = "Type your message here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, "", "Fw: "..message.subject, fwfooter) + + elseif fields.markread then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = false + end + mail.show_inbox(name) + + elseif fields.markunread then + if messages[selected_message_idxs[name]] then + messages[selected_message_idxs[name]].unread = true + end + mail.show_inbox(name) + + elseif fields.new then + mail.show_compose(name,"","","Type your message here.") + + elseif fields.quit then + if minetest.get_modpath("unified_inventory") then + unified_inventory.set_inventory_formspec(player, "craft") + end + + elseif fields.about then + mail.show_about(name) + + end + + mail.setMessages(name, messages) + return true + elseif formname == "mail:message" then + local name = player:get_player_name() + local messages = mail.getMessages(name) + + if fields.back then + mail.show_inbox(name) + elseif fields.reply then + local message = messages[selected_message_idxs[name]] + local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, message.sender, "Re: "..message.subject, replyfooter) + elseif fields.forward then + local message = messages[selected_message_idxs[name]] + local fwfooter = "Type your message here.\n\n--Original message follows--\n" ..message.body + mail.show_compose(name, "", "Fw: "..message.subject, fwfooter) + elseif fields.delete then + if messages[selected_message_idxs[name]] then + table.remove(messages,selected_message_idxs[name]) + end + mail.show_inbox(name) + end + + mail.setMessages(name, messages) + return true + elseif formname == "mail:compose" then + if fields.send then + mail.send({ + src = player:get_player_name(), + dst = fields.to, + subject = fields.subject, + body = fields.body + }) + end + minetest.after(0.5, function() + mail.show_inbox(player:get_player_name()) + end) + return true + + elseif fields.mail then + mail.show_inbox(player:get_player_name()) + else + return false + end +end + +minetest.register_on_player_receive_fields(mail.handle_receivefields) + + +if minetest.get_modpath("unified_inventory") then + mail.receive_mail_message = mail.receive_mail_message .. + " or use the mail button in the inventory" + mail.read_later_message = mail.read_later_message .. + " or by using the mail button in the inventory" + + unified_inventory.register_button("mail", { + type = "image", + image = "mail_button.png", + tooltip = "Mail" + }) +end diff --git a/hud.lua b/hud.lua new file mode 100644 index 0000000..bb07ca2 --- /dev/null +++ b/hud.lua @@ -0,0 +1,59 @@ + +local huddata = {} + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + local data = {} + + data.imageid = player:hud_add({ + hud_elem_type = "image", + name = "MailIcon", + position = {x=0.52, y=0.52}, + text="", + scale = {x=1,y=1}, + alignment = {x=0.5, y=0.5}, + }) + + data.textid = player:hud_add({ + hud_elem_type = "text", + name = "MailText", + position = {x=0.55, y=0.52}, + text= "", + scale = {x=1,y=1}, + alignment = {x=0.5, y=0.5}, + }) + + + huddata[name] = data +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + huddata[name] = nil +end) + + +mail.hud_update = function(playername, messages) + local data = huddata[playername] + local player = minetest.get_player_by_name(playername) + + if not data or not player then + return + end + + local unreadcount = 0 + for _, message in ipairs(messages) do + if message.unread then + unreadcount = unreadcount + 1 + end + end + + if unreadcount == 0 then + player:hud_change(data.imageid, "text", "") + player:hud_change(data.textid, "text", "") + else + player:hud_change(data.imageid, "text", "email_mail.png") + player:hud_change(data.textid, "text", unreadcount .. " /mail") + end + +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..c5c08e2 --- /dev/null +++ b/init.lua @@ -0,0 +1,55 @@ +mail = { + -- mail directory + maildir = minetest.get_worldpath().."/mails", + + -- allow item/node attachments + allow_attachments = minetest.settings:get("mail.allow_attachments") == "true", + + webmail = { + -- disallow banned players in the webmail interface + disallow_banned_players = minetest.settings:get("webmail.disallow_banned_players") == "true", + + -- url and key to the webmail server + url = minetest.settings:get("webmail.url"), + key = minetest.settings:get("webmail.key") + }, + + tan = {} +} + + +local MP = minetest.get_modpath(minetest.get_current_modname()) +dofile(MP .. "/chatcommands.lua") +dofile(MP .. "/migrate.lua") +dofile(MP .. "/attachment.lua") +dofile(MP .. "/hud.lua") +dofile(MP .. "/storage.lua") +dofile(MP .. "/api.lua") +dofile(MP .. "/gui.lua") +dofile(MP .. "/onjoin.lua") + +-- optional webmail stuff below + +--[[ minetest.conf +secure.http_mods = mail +webmail.url = http://127.0.0.1:8080 +webmail.key = myserverkey +--]] + +local http = minetest.request_http_api() + +if http then + local webmail_url = mail.webmail.url + local webmail_key = mail.webmail.key + + if not webmail_url then error("webmail.url is not defined") end + if not webmail_key then error("webmail.key is not defined") end + + print("[mail] loading webmail-component with endpoint: " .. webmail_url) + dofile(MP .. "/tan.lua") + dofile(MP .. "/webmail.lua") + mail.webmail_init(http, webmail_url, webmail_key) +end + +-- migrate storage +mail.migrate() diff --git a/migrate.lua b/migrate.lua new file mode 100644 index 0000000..5a14478 --- /dev/null +++ b/migrate.lua @@ -0,0 +1,24 @@ + +-- migrate from mail.db to player-file-based mailbox + +mail.migrate = function() + + local file = io.open(minetest.get_worldpath().."/mail.db", "r") + if file then + print("[mail] migrating to new per-player storage") + minetest.mkdir(mail.maildir) + + local data = file:read("*a") + local oldmails = minetest.deserialize(data) + file:close() + + for name, oldmessages in pairs(oldmails) do + mail.setMessages(name, oldmessages) + end + + -- rename file + print("[mail] migration done, renaming old mail.db") + os.rename(minetest.get_worldpath().."/mail.db", minetest.get_worldpath().."/mail.db.old") + end + +end diff --git a/onjoin.lua b/onjoin.lua new file mode 100644 index 0000000..79ad287 --- /dev/null +++ b/onjoin.lua @@ -0,0 +1,19 @@ +minetest.register_on_joinplayer(function(player) + minetest.after(2, function(name) + local messages = mail.getMessages(name) + + local unreadcount = 0 + + for _, message in pairs(messages) do + if message.unread then + unreadcount = unreadcount + 1 + end + end + + if unreadcount > 0 then + minetest.chat_send_player(name, + "(" .. unreadcount .. ") You have mail! Type /mail to read") + + end + end, player:get_player_name()) +end) diff --git a/storage.lua b/storage.lua new file mode 100644 index 0000000..8f50a74 --- /dev/null +++ b/storage.lua @@ -0,0 +1,32 @@ + +-- TODO: maybe local cache? + +function getMailFile(playername) + local saneplayername = string.gsub(playername, "[.|/]", "") + return mail.maildir .. "/" .. saneplayername .. ".json" +end + +mail.getMessages = function(playername) + local file = io.open(getMailFile(playername), "r") + local messages = {} + if file then + local json = file:read("*a") + messages = minetest.parse_json(json or "[]") or {} + mail.hud_update(playername, messages) + file:close() + end + + return messages +end + +mail.setMessages = function(playername, messages) + local file = io.open(getMailFile(playername),"w") + local json = minetest.write_json(messages) + if file and file:write(json) and file:close() then + mail.hud_update(playername, messages) + return true + else + minetest.log("error","[mail] Save failed - messages may be lost!") + return false + end +end diff --git a/tan.lua b/tan.lua new file mode 100644 index 0000000..95d116c --- /dev/null +++ b/tan.lua @@ -0,0 +1,16 @@ + + +minetest.register_chatcommand("webmail_tan", { + description = "generates a tan (temporary access number) for the webmail access", + func = function(name) + local tan = "" .. math.random(1000, 9999) + mail.tan[name] = tan + + return true, "Your tan is " .. tan .. ", it will expire upon leaving the game" + end +}) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + mail.tan[name] = nil +end) diff --git a/textures/email_mail.png b/textures/email_mail.png new file mode 100644 index 0000000..b1e5553 Binary files /dev/null and b/textures/email_mail.png differ diff --git a/textures/mail_button.png b/textures/mail_button.png new file mode 100644 index 0000000..8adf3ea Binary files /dev/null and b/textures/mail_button.png differ diff --git a/util/channel.lua b/util/channel.lua new file mode 100644 index 0000000..3b5111b --- /dev/null +++ b/util/channel.lua @@ -0,0 +1,93 @@ +-- bi-directional http-channel +-- with long-poll GET and POST on the same URL + +local debug = false + +local function Channel(http, url, cfg) + cfg = cfg or {} + local extra_headers = cfg.extra_headers or {} + local timeout = cfg.timeout or 1 + local long_poll_timeout = cfg.long_poll_timeout or 30 + local error_retry = cfg.error_retry or 10 + + -- assemble post-header with json content + local post_headers = { "Content-Type: application/json" } + for _,header in pairs(cfg.extra_headers) do + table.insert(post_headers, header) + end + + local recv_listeners = {} + local run = true + + local recv_loop + + recv_loop = function() + assert(run) + + -- long-poll GET + http.fetch({ + url = url, + extra_headers = extra_headers, + timeout = long_poll_timeout + }, function(res) + if res.succeeded and res.code == 200 then + local data = minetest.parse_json(res.data) + + if debug then + minetest.log("action", "[webmail-rx] " .. dump(data)) + end + + if data then + for _,listener in pairs(recv_listeners) do + listener(data) + end + end + -- reschedule immediately + minetest.after(0, recv_loop) + else + -- error, retry after some time + minetest.after(error_retry, recv_loop) + end + end) + end + + + local send = function(data) + assert(run) + -- POST + + if debug then + minetest.log("action", "[webmail-tx] " .. dump(data)) + end + + http.fetch({ + url = url, + extra_headers = post_headers, + timeout = timeout, + post_data = minetest.write_json(data) + }, function(res) + -- TODO: error-handling + end) + end + + local receive = function(listener) + table.insert(recv_listeners, listener) + end + + local close = function() + run = false + end + + recv_loop(); + + return { + send = send, + receive = receive, + close = close + } + +end + + + +return Channel diff --git a/webmail.lua b/webmail.lua new file mode 100644 index 0000000..3bcc9ff --- /dev/null +++ b/webmail.lua @@ -0,0 +1,156 @@ +-- false per default +local has_xban2_mod = minetest.get_modpath("xban2") + +local MP = minetest.get_modpath(minetest.get_current_modname()) +local Channel = dofile(MP .. "/util/channel.lua") +local channel + +-- auth request from webmail +local function auth_handler(data) + local auth = data.params + local handler = minetest.get_auth_handler() + minetest.log("action", "[webmail] auth: " .. auth.playername) + + local success = false + local banned = false + local message = "" + + if mail.webmail.disallow_banned_players and has_xban2_mod then + -- check xban db + local xbanentry = xban.find_entry(auth.playername) + if xbanentry and xbanentry.banned then + banned = true + message = "Banned!" + end + end + + if not banned then + -- check tan + local tan = mail.tan[auth.playername] + if tan ~= nil then + success = tan == auth.password + end + + -- check auth + if not success then + local entry = handler.get_auth(auth.playername) + if entry and minetest.check_password_entry(auth.playername, entry.password, auth.password) then + success = true + end + end + end + + channel.send({ + method = data.method, + id = data.id, + result = { + success = success, + message = message + } + }) +end + +-- send request from webmail +local function send_handler(data) + -- send mail from webclient + if not data.params then + return + end + + minetest.log("action", "[webmail] sending mail from webclient: " .. data.params.sender .. + " -> " .. data.params.receiver) + + mail.send(data.params) + + channel.send({ + method = data.method, + id = data.id, + result = { + success = true + } + }) +end + +-- get player messages request from webmail +local function get_player_messages_handler(data) + local messages = mail.getMessages(data.params.playername) + channel.send({ + method = data.method, + id = data.id, + result = messages + }) +end + +-- remove mail +local function delete_mail_handler(data) + local index = data.params.index + local playername = data.params.playername + + local messages = mail.getMessages(playername) + if messages[index] then + table.remove(messages, index) + end + mail.setMessages(playername, messages) + -- TODO: check subject + + channel.send({ + method = data.method, + id = data.id, + result = { success = true } + }) +end + +-- mark mail as read +local function mark_mail_read_handler(data) + local index = data.params.index + local playername = data.params.playername + local read = data.params.read + + local messages = mail.getMessages(playername) + + if messages[index] then + messages[index].unread = not read + end + mail.setMessages(playername, messages) + -- TODO: check subject + + channel.send({ + method = data.method, + id = data.id, + result = { success = true } + }) +end + +function mail.webmail_send_hook(m) + channel.send({ + type = "new-message", + data = m + }) +end +mail.register_on_receive(mail.webmail_send_hook) + +function mail.webmail_init(http, url, key) + channel = Channel(http, url .. "/api/minetest/channel", { + extra_headers = { "webmailkey: " .. key } + }) + + channel.receive(function(data) + if data.method == "auth" then + auth_handler(data) + + elseif data.method == "get-mails" then + get_player_messages_handler(data) + + elseif data.method == "mark-mail-read" then + mark_mail_read_handler(data) + + elseif data.method == "delete-mail" then + delete_mail_handler(data) + + elseif data.method == "send" then + send_handler(data) + + + end + end) +end