Mercurial > prosody-modules
comparison mod_invite/mod_invite.lua @ 2058:4b3037c7af62
mod_invite: Allows existing users to generate URLs that can be used to invite new users. Mutual presence subscriptions are automatically created when the creation succeeds.
| author | Thijs Alkemade <me@thijsalkema.de> |
|---|---|
| date | Wed, 02 Mar 2016 18:12:34 +0100 |
| parents | |
| children | dc4e77824318 |
comparison
equal
deleted
inserted
replaced
| 2057:1c126c49f5c1 | 2058:4b3037c7af62 |
|---|---|
| 1 local adhoc_new = module:require "adhoc".new; | |
| 2 local uuid_new = require "util.uuid".generate; | |
| 3 local jid_split = require "util.jid".split; | |
| 4 local jid_join = require "util.jid".join; | |
| 5 local http_formdecode = require "net.http".formdecode; | |
| 6 local http_urlencode = require "net.http".urlencode; | |
| 7 local usermanager = require "core.usermanager"; | |
| 8 local rostermanager = require "core.rostermanager"; | |
| 9 local nodeprep = require "util.encodings".stringprep.nodeprep; | |
| 10 local tostring = tostring; | |
| 11 | |
| 12 local invite_storage = module:open_store(); | |
| 13 local inviter_storage = module:open_store("inviter"); | |
| 14 | |
| 15 local serve = module:depends"http_files".serve; | |
| 16 | |
| 17 module:depends"http"; | |
| 18 | |
| 19 local entities = { | |
| 20 ["<"] = "<", [">"] = ">", ["&"] = "&", | |
| 21 ["'"] = "'", ["\""] = """, ["\n"] = "<br/>", | |
| 22 }; | |
| 23 | |
| 24 local function tohtml(plain) | |
| 25 return (plain:gsub("[<>&'\"\n]", entities)); | |
| 26 end | |
| 27 | |
| 28 local function apply_template(template, args) | |
| 29 return | |
| 30 template:gsub("{{([^}]*)}}", function (k) | |
| 31 if args[k] then | |
| 32 return tohtml(args[k]) | |
| 33 else | |
| 34 return k | |
| 35 end | |
| 36 end) | |
| 37 end | |
| 38 | |
| 39 function generate_page(event, display_options) | |
| 40 local request, response = event.request, event.response; | |
| 41 | |
| 42 local tokens = invite_storage:get() or {}; | |
| 43 | |
| 44 local token = request.path:match("^/invite/([^/]*)$"); | |
| 45 | |
| 46 response.headers.content_type = "text/html; charset=utf-8"; | |
| 47 | |
| 48 if not token or not tokens[token] then | |
| 49 local template = assert(module:load_resource("invite/invite_result.html")):read("*a"); | |
| 50 | |
| 51 return apply_template(template, { classes = "alert-danger", message = "This invite has expired." }) | |
| 52 end | |
| 53 | |
| 54 local template = assert(module:load_resource("invite/invite.html")):read("*a"); | |
| 55 | |
| 56 return apply_template(template, { user = jid_join(tokens[token], module.host), server = module.host, token = token }); | |
| 57 end | |
| 58 | |
| 59 function subscribe(user1, user2) | |
| 60 local user1_jid = jid_join(user1, module.host); | |
| 61 local user2_jid = jid_join(user2, module.host); | |
| 62 | |
| 63 rostermanager.set_contact_pending_out(user2, module.host, user1_jid); | |
| 64 rostermanager.set_contact_pending_in(user1, module.host, user2_jid); | |
| 65 rostermanager.subscribed(user1, module.host, user2_jid); | |
| 66 rostermanager.process_inbound_subscription_approval(user2, module.host, user1_jid); | |
| 67 end | |
| 68 | |
| 69 function handle_form(event, display_options) | |
| 70 local request, response = event.request, event.response; | |
| 71 local form_data = http_formdecode(request.body); | |
| 72 local user, password, token = form_data["user"], form_data["password"], form_data["token"]; | |
| 73 local tokens = invite_storage:get() or {}; | |
| 74 | |
| 75 local template = assert(module:load_resource("invite/invite_result.html")):read("*a"); | |
| 76 | |
| 77 response.headers.content_type = "text/html; charset=utf-8"; | |
| 78 | |
| 79 if not user or #user == 0 or not password or #password == 0 or not token then | |
| 80 return apply_template(template, { classes = "alert-warning", message = "Please fill in all fields." }) | |
| 81 end | |
| 82 | |
| 83 if not tokens[token] then | |
| 84 return apply_template(template, { classes = "alert-danger", message = "This invite has expired." }) | |
| 85 end | |
| 86 | |
| 87 -- Shamelessly copied from mod_register_web. | |
| 88 local prepped_username = nodeprep(user); | |
| 89 | |
| 90 if not prepped_username or #prepped_username == 0 then | |
| 91 return apply_template(template, { classes = "alert-warning", message = "This username contains invalid characters." }) | |
| 92 end | |
| 93 | |
| 94 if usermanager.user_exists(prepped_username, module.host) then | |
| 95 return apply_template(template, { classes = "alert-warning", message = "This username is already in use." }) | |
| 96 end | |
| 97 | |
| 98 local registering = { username = prepped_username , host = module.host, allowed = true } | |
| 99 | |
| 100 module:fire_event("user-registering", registering); | |
| 101 | |
| 102 if not registering.allowed then | |
| 103 return apply_template(template, { classes = "alert-danger", message = "Registration is not allowed." }) | |
| 104 end | |
| 105 | |
| 106 local ok, err = usermanager.create_user(prepped_username, password, module.host); | |
| 107 | |
| 108 if ok then | |
| 109 subscribe(prepped_username, tokens[token]); | |
| 110 subscribe(tokens[token], prepped_username); | |
| 111 | |
| 112 inviter_storage:set(prepped_username, { inviter = tokens[token] }); | |
| 113 | |
| 114 rostermanager.roster_push(tokens[token], module.host, jid_join(prepped_username, module.host)); | |
| 115 | |
| 116 tokens[token] = nil; | |
| 117 | |
| 118 invite_storage:set(nil, tokens); | |
| 119 | |
| 120 return apply_template(template, { classes = "alert-success", message = "Your account has been created! You can now log in using an XMPP client." }) | |
| 121 else | |
| 122 module:log("debug", "Registration failed: " .. tostring(err)); | |
| 123 | |
| 124 return apply_template(template, { classes = "alert-danger", message = "An unknown error has occurred." }) | |
| 125 end | |
| 126 end | |
| 127 | |
| 128 module:provides("http", { | |
| 129 route = { | |
| 130 ["GET /a_file.txt"] = serve(module:get_directory().."/my_file.txt"); | |
| 131 ["GET /bootstrap.min.css"] = serve(module:get_directory()); | |
| 132 ["GET /*"] = generate_page; | |
| 133 POST = handle_form; | |
| 134 }; | |
| 135 }); | |
| 136 | |
| 137 function invite_command_handler(self, data, state) | |
| 138 local uuid = uuid_new(); | |
| 139 | |
| 140 local user, host = jid_split(data.from); | |
| 141 | |
| 142 if host ~= module.host then | |
| 143 return { status = "completed", error = { message = "You are not allowed to invite users to this server." }}; | |
| 144 end | |
| 145 | |
| 146 local tokens = invite_storage:get() or {}; | |
| 147 | |
| 148 tokens[uuid] = user; | |
| 149 | |
| 150 invite_storage:set(nil, tokens); | |
| 151 | |
| 152 return { info = module:http_url() .. "/" .. uuid, status = "completed" }; | |
| 153 end | |
| 154 | |
| 155 local adhoc_invite = adhoc_new("Invite user", "invite", invite_command_handler, "user") | |
| 156 | |
| 157 module:add_item("adhoc", adhoc_invite); |
