init.lua 5.2 KB

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