Software /
code /
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 |
child | 2238: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); |