init.lua 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. -- Mod is reloadable.
  2. if not minetest.global_exists("email") then email = {} end
  3. email.inboxes = email.inboxes or {}
  4. email.modpath = minetest.get_modpath("email")
  5. email.worldpath = minetest.get_worldpath()
  6. email.database = email.worldpath .. "/email.sqlite"
  7. email.maxsize = 100
  8. email.max_subject_length = 128
  9. email.max_message_length = 1024*2
  10. -- Localize for performance.
  11. local math_random = math.random
  12. dofile(email.modpath .. "/database.lua")
  13. function email.on_startup()
  14. if not email.sql then
  15. return
  16. end
  17. assert(not email.db)
  18. email.db = email.sql.open(email.database)
  19. assert(email.db)
  20. -- Ensure table exists.
  21. email.db_exec([[ CREATE TABLE IF NOT EXISTS email (
  22. name TEXT,
  23. sender TEXT,
  24. date TEXT,
  25. subject TEXT,
  26. message TEXT,
  27. number INTEGER
  28. ); ]])
  29. end
  30. function email.on_shutdown()
  31. if email.db then
  32. email.db:close()
  33. email.db = nil
  34. end
  35. end
  36. function email.get_inbox(name)
  37. if not email.db then
  38. return {}
  39. end
  40. name = rename.grn(name)
  41. if not email.inboxes[name] then
  42. assert(email.db)
  43. email.inboxes[name] = email.load_inbox(email.db, name) or {}
  44. end
  45. return email.inboxes[name]
  46. end
  47. function email.get_inbox_size(name)
  48. name = rename.grn(name)
  49. local tb = email.get_inbox(name)
  50. return #tb
  51. end
  52. function email.clear_inbox(name, mails)
  53. if not email.db then
  54. return
  55. end
  56. name = rename.grn(name)
  57. local inbox = email.get_inbox(name)
  58. -- `mails` is a list of emails to delete.
  59. -- If an email has a duplicate in the `mails` table, then we delete it.
  60. for k, v in ipairs(mails) do
  61. local rng = v.rng
  62. assert(type(rng) == "number")
  63. for i, j in ipairs(inbox) do
  64. local rng2 = j.rng
  65. assert(type(rng2) == "number")
  66. if rng2 == rng then
  67. table.remove(inbox, i)
  68. break
  69. end
  70. end
  71. end
  72. email.delete_mails(email.db, name, mails)
  73. if #inbox == 0 then
  74. -- This will cause a refetch from database.
  75. email.inboxes[name] = nil
  76. end
  77. end
  78. function email.send_mail_multi(from, multi_target, subject, message)
  79. from = rename.grn(from)
  80. local success = {}
  81. local failure = {}
  82. if not email.db then
  83. return success, failure
  84. end
  85. email.db_exec([[ BEGIN TRANSACTION; ]])
  86. -- Assume multi-target is a table of playernames to send mail to.
  87. for k, v in ipairs(multi_target) do
  88. v = rename.grn(v)
  89. local bresult, sresult = email.send_mail_ex(from, v, subject, message)
  90. if bresult == true then
  91. table.insert(success, {name=v, error=sresult})
  92. elseif bresult == false then
  93. table.insert(failure, {name=v, error=sresult})
  94. end
  95. end
  96. email.db_exec([[ COMMIT; ]])
  97. return success, failure -- Tables with success and failure results.
  98. end
  99. -- API function. Shall send mail to a player, first checking if
  100. -- sending is possible and permitted.
  101. -- Returns a success boolean and a string key.
  102. function email.send_mail_single(from, to, subject, message)
  103. from = rename.grn(from)
  104. to = rename.grn(to)
  105. local bresult, sresult = email.send_mail_ex(from, to, subject, message)
  106. return bresult, sresult -- Boolean, error key.
  107. end
  108. -- This should only be called internally to this mod.
  109. function email.send_mail_ex(from, to, subject, message)
  110. if not email.db then
  111. return false, "missingdep"
  112. end
  113. from = rename.grn(from)
  114. to = rename.grn(to)
  115. -- Cannot send email if recipient does not exist.
  116. if not minetest.player_exists(to) then
  117. return false, "badplayer"
  118. end
  119. if string.len(subject) > email.max_subject_length or string.len(message) > email.max_message_length then
  120. return false, "toobig"
  121. end
  122. local inboxes = email.get_inbox(to)
  123. if #inboxes >= email.maxsize then return false, "boxfull" end -- Inbox full.
  124. -- Find a unique ID for this new email.
  125. ::tryagain::
  126. local rng = math_random(1, 32000) -- 0 is not a valid ID. Important!
  127. for k, v in ipairs(inboxes) do
  128. if v.rng == rng then goto tryagain end
  129. end
  130. local mail = {
  131. date = os.date("%Y-%m-%d"),
  132. from = from,
  133. msg = message,
  134. sub = subject,
  135. rng = rng, -- Random ID unique from all other emails in this inbox.
  136. }
  137. -- Keep in-memory cache sychronized with database.
  138. -- Only if cache already loaded for this target player.
  139. if email.inboxes[to] then
  140. table.insert(email.inboxes[to], mail)
  141. end
  142. email.store_mail(email.db, to, mail)
  143. return true, "success" -- Mail successfully sent!
  144. end
  145. function email.on_joinplayer(player)
  146. local pname = player:get_player_name()
  147. minetest.after(10, function()
  148. if passport.player_registered(pname) then
  149. local inbox = email.get_inbox(pname)
  150. if #inbox > 0 then
  151. minetest.chat_send_player(pname,
  152. "# Server: You have mail (" .. #inbox .. ")! Use a Key of Citizenship to view it.")
  153. end
  154. end
  155. end)
  156. end
  157. if not email.registered then
  158. -- load insecure environment
  159. local secenv = minetest.request_insecure_environment()
  160. if secenv then
  161. print("[email] insecure environment loaded.")
  162. local success, lib = pcall(secenv.require, "lsqlite3")
  163. if not success then
  164. minetest.log("error", "lsqlite3 failed to load. email functionality disabled.")
  165. minetest.log("error", lib)
  166. else
  167. assert(lib.open)
  168. email.sql = lib
  169. end
  170. else
  171. minetest.log("error", "[email] Failed to load insecure environment," ..
  172. " please add this mod to the trusted mods list.")
  173. end
  174. -- Don't allow other mods to use this global library!
  175. if sqlite3 then sqlite3 = nil end
  176. minetest.register_on_shutdown(function(...)
  177. return email.on_shutdown(...)
  178. end)
  179. minetest.register_on_joinplayer(function(...)
  180. return email.on_joinplayer(...)
  181. end)
  182. email.on_startup()
  183. dofile(email.modpath .. "/hud.lua")
  184. local c = "email:core"
  185. local f = email.modpath .. "/init.lua"
  186. reload.register_file(c, f, false)
  187. email.registered = true
  188. end