-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod_vjud.lua
189 lines (167 loc) · 5.75 KB
/
mod_vjud.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
local dm_load = require "util.datamanager".load;
local dm_store = require "util.datamanager".store;
local usermanager = require "core.usermanager";
local dataforms_new = require "util.dataforms".new;
local jid_split = require "util.jid".prepped_split;
local vcard = module:require "vcard";
local rawget, rawset = rawget, rawset;
local s_lower = string.lower;
local s_find = string.find;
local st = require "util.stanza";
local template = require "util.template";
local instructions = module:get_option_string("vjud_instructions", "Fill in one or more fields to search for any matching Jabber users.");
local get_reply = template[[
<query xmlns="jabber:iq:search">
<instructions>{instructions}</instructions>
<first/>
<last/>
<nick/>
<email/>
</query>
]].apply({ instructions = instructions });
local item_template = template[[
<item xmlns="jabber:iq:search" jid="{jid}">
<first>{first}</first>
<last>{last}</last>
<nick>{nick}</nick>
<email>{email}</email>
</item>
]];
local search_mode = module:get_option_string("vjud_mode", "opt-in");
local allow_remote = module:get_option_boolean("allow_remote_searches", search_mode ~= "all");
local base_host = module:get_option_string("vjud_search_domain",
module:get_host_type() == "component"
and module.host:gsub("^[^.]+%.","")
or module.host);
module:depends"disco";
if module:get_host_type() == "component" then
module:add_identity("directory", "user", module:get_option_string("name", "User search"));
end
module:add_feature("jabber:iq:search");
local vCard_mt = {
__index = function(t, k)
if type(k) ~= "string" then return nil end
for i=1,#t do
local t_i = rawget(t, i);
if t_i and t_i.name == k then
rawset(t, k, t_i);
return t_i;
end
end
end
};
local function get_user_vcard(user, host)
local vCard = dm_load(user, host or base_host, "vcard");
if vCard then
vCard = st.deserialize(vCard);
vCard = vcard.from_xep54(vCard);
return setmetatable(vCard, vCard_mt);
end
end
local at_host = "@"..base_host;
local users; -- The user iterator
module:hook("iq/host/jabber:iq:search:query", function(event)
local origin, stanza = event.origin, event.stanza;
if not (allow_remote or origin.type == "c2s") then
origin.send(st.error_reply(stanza, "cancel", "not-allowed"))
return true;
end
if stanza.attr.type == "get" then
origin.send(st.reply(stanza):add_child(get_reply));
else -- type == "set"
local query = stanza.tags[1];
local first, last, nick, email =
s_lower(query:get_child_text"first" or ""),
s_lower(query:get_child_text"last" or ""),
s_lower(query:get_child_text"nick" or ""),
s_lower(query:get_child_text"email" or "");
first = #first >= 2 and first;
last = #last >= 2 and last;
nick = #nick >= 2 and nick;
email = #email >= 2 and email;
if not ( first or last or nick or email ) then
origin.send(st.error_reply(stanza, "modify", "not-acceptable", "All fields were empty or too short"));
return true;
end
local reply = st.reply(stanza):query("jabber:iq:search");
local username, hostname = jid_split(email);
if hostname == base_host and username and usermanager.user_exists(username, hostname) then
local vCard = get_user_vcard(username);
if vCard then
reply:add_child(item_template.apply{
jid = username..at_host;
first = vCard.N and vCard.N[2] or nil;
last = vCard.N and vCard.N[1] or nil;
nick = vCard.NICKNAME and vCard.NICKNAME[1] or username;
email = vCard.EMAIL and vCard.EMAIL[1] or nil;
});
end
else
for username in users() do
local vCard = get_user_vcard(username);
if vCard
and ((first and vCard.N and s_find(s_lower(vCard.N[2]), first, nil, true))
or (last and vCard.N and s_find(s_lower(vCard.N[1]), last, nil, true))
or (nick and vCard.NICKNAME and s_find(s_lower(vCard.NICKNAME[1]), nick, nil, true))
or (email and vCard.EMAIL and s_find(s_lower(vCard.EMAIL[1]), email, nil, true))) then
reply:add_child(item_template.apply{
jid = username..at_host;
first = vCard.N and vCard.N[2] or nil;
last = vCard.N and vCard.N[1] or nil;
nick = vCard.NICKNAME and vCard.NICKNAME[1] or username;
email = vCard.EMAIL and vCard.EMAIL[1] or nil;
});
end
end
end
origin.send(reply);
end
return true;
end);
if search_mode == "all" then
function users()
return usermanager.users(base_host);
end
else -- if "opt-in", default
local opted_in;
function module.load()
opted_in = dm_load(nil, module.host, "user_index") or {};
end
function module.unload()
dm_store(nil, module.host, "user_index", opted_in);
end
function users()
return pairs(opted_in);
end
local opt_in_layout = dataforms_new{
title = "Search settings";
instructions = "Do you want to appear in search results?";
{
name = "searchable",
label = "Appear in search results?",
type = "boolean",
},
};
local function opt_in_handler(self, data, state)
local username, hostname = jid_split(data.from);
if state then -- the second return value
if data.action == "cancel" then
return { status = "canceled" };
end
if not username or not hostname or hostname ~= base_host then
return { status = "error", error = { type = "cancel",
condition = "forbidden", message = "Invalid user or hostname." } };
end
local fields = opt_in_layout:data(data.form);
opted_in[username] = fields.searchable or nil
return { status = "completed" }
else -- No state, send the form.
return { status = "executing", actions = { "complete" },
form = { layout = opt_in_layout, values = { searchable = opted_in[username] } } }, true;
end
end
local adhoc_new = module:require "adhoc".new;
local adhoc_vjudsetup = adhoc_new("Search settings", "vjudsetup", opt_in_handler);--, "self");-- and nil);
module:depends"adhoc";
module:provides("adhoc", adhoc_vjudsetup);
end