author | Kim Alvefur <zash@zash.se> |
Tue, 16 Sep 2014 19:53:41 +0200 | |
changeset 1506 | a40f9b8661d8 |
parent 1498 | e82592ed744b |
child 1521 | 71af9c272d72 |
permissions | -rw-r--r-- |
1471 | 1 |
-- Prosody IM |
2 |
-- |
|
3 |
-- This project is MIT/X11 licensed. Please see the |
|
4 |
-- COPYING file in the source package for more information. |
|
5 |
-- |
|
6 |
local get_prefs = module:require"mod_mam/mamprefs".get; |
|
7 |
local set_prefs = module:require"mod_mam/mamprefs".set; |
|
8 |
local rsm = module:require "mod_mam/rsm"; |
|
9 |
local jid_bare = require "util.jid".bare; |
|
10 |
local jid_prep = require "util.jid".prep; |
|
11 |
local date_parse = require "util.datetime".parse; |
|
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
12 |
local date_format = require "util.datetime".datetime; |
1471 | 13 |
|
14 |
local st = require "util.stanza"; |
|
15 |
local archive_store = "archive2"; |
|
16 |
local archive = module:open_store(archive_store, "archive"); |
|
17 |
local global_default_policy = module:get_option("default_archive_policy", false); |
|
18 |
local default_max_items, max_max_items = 20, module:get_option_number("max_archive_query_results", 50); |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
19 |
local conversation_interval = tonumber(module:get_option_number("archive_conversation_interval", 86400)); |
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
20 |
local resolve_relative_path = require "core.configmanager".resolve_relative_path; |
1471 | 21 |
|
22 |
-- Feature discovery |
|
23 |
local xmlns_archive = "urn:xmpp:archive" |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
24 |
local feature_archive = st.stanza("feature", {xmlns=xmlns_archive}):tag("optional"); |
1471 | 25 |
if(global_default_policy) then |
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
26 |
feature_archive:tag("default"); |
1471 | 27 |
end |
28 |
module:add_extension(feature_archive); |
|
29 |
module:add_feature("urn:xmpp:archive:auto"); |
|
30 |
module:add_feature("urn:xmpp:archive:manage"); |
|
31 |
module:add_feature("urn:xmpp:archive:pref"); |
|
32 |
module:add_feature("http://jabber.org/protocol/rsm"); |
|
33 |
-- -------------------------------------------------- |
|
34 |
||
35 |
local function prefs_to_stanza(prefs) |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
36 |
local prefstanza = st.stanza("pref", { xmlns="urn:xmpp:archive" }); |
1471 | 37 |
local default = prefs[false] ~= nil and prefs[false] or global_default_policy; |
38 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
39 |
prefstanza:tag("default", {otr="oppose", save=default and "true" or "false"}):up(); |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
40 |
prefstanza:tag("method", {type="auto", use="concede"}):up(); |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
41 |
prefstanza:tag("method", {type="local", use="concede"}):up(); |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
42 |
prefstanza:tag("method", {type="manual", use="concede"}):up(); |
1471 | 43 |
|
44 |
for jid, choice in pairs(prefs) do |
|
45 |
if jid then |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
46 |
prefstanza:tag("item", {jid=jid, otr="prefer", save=choice and "message" or "false" }):up() |
1471 | 47 |
end |
48 |
end |
|
49 |
||
50 |
return prefstanza; |
|
51 |
end |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
52 |
local function prefs_from_stanza(stanza, username) |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
53 |
local current_prefs = get_prefs(username); |
1471 | 54 |
|
55 |
-- "default" | "item" | "session" | "method" |
|
56 |
for elem in stanza:children() do |
|
57 |
if elem.name == "default" then |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
58 |
current_prefs[false] = elem.attr["save"] == "true"; |
1471 | 59 |
elseif elem.name == "item" then |
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
60 |
current_prefs[elem.attr["jid"]] = not elem.attr["save"] == "false"; |
1471 | 61 |
elseif elem.name == "session" then |
62 |
module:log("info", "element is not supported: " .. tostring(elem)); |
|
63 |
-- local found = false; |
|
64 |
-- for child in data:children() do |
|
65 |
-- if child.name == elem.name and child.attr["thread"] == elem.attr["thread"] then |
|
66 |
-- for k, v in pairs(elem.attr) do |
|
67 |
-- child.attr[k] = v; |
|
68 |
-- end |
|
69 |
-- found = true; |
|
70 |
-- break; |
|
71 |
-- end |
|
72 |
-- end |
|
73 |
-- if not found then |
|
74 |
-- data:tag(elem.name, elem.attr):up(); |
|
75 |
-- end |
|
76 |
elseif elem.name == "method" then |
|
77 |
module:log("info", "element is not supported: " .. tostring(elem)); |
|
78 |
-- local newpref = stanza.tags[1]; -- iq:pref |
|
79 |
-- for _, e in ipairs(newpref.tags) do |
|
80 |
-- -- if e.name ~= "method" then continue end |
|
81 |
-- local found = false; |
|
82 |
-- for child in data:children() do |
|
83 |
-- if child.name == "method" and child.attr["type"] == e.attr["type"] then |
|
84 |
-- child.attr["use"] = e.attr["use"]; |
|
85 |
-- found = true; |
|
86 |
-- break; |
|
87 |
-- end |
|
88 |
-- end |
|
89 |
-- if not found then |
|
90 |
-- data:tag(e.name, e.attr):up(); |
|
91 |
-- end |
|
92 |
-- end |
|
93 |
end |
|
94 |
end |
|
95 |
end |
|
96 |
||
97 |
------------------------------------------------------------ |
|
98 |
-- Preferences |
|
99 |
------------------------------------------------------------ |
|
100 |
local function preferences_handler(event) |
|
101 |
local origin, stanza = event.origin, event.stanza; |
|
102 |
local user = origin.username; |
|
103 |
local reply = st.reply(stanza); |
|
104 |
||
105 |
if stanza.attr.type == "get" then |
|
106 |
reply:add_child(prefs_to_stanza(get_prefs(user))); |
|
107 |
end |
|
108 |
if stanza.attr.type == "set" then |
|
109 |
local new_prefs = stanza:get_child("pref", xmlns_archive); |
|
110 |
if not new_prefs then return false; end |
|
111 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
112 |
local prefs = prefs_from_stanza(stanza, origin.username); |
1471 | 113 |
local ok, err = set_prefs(user, prefs); |
114 |
||
115 |
if not ok then |
|
116 |
return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); |
|
117 |
end |
|
118 |
end |
|
119 |
return origin.send(reply); |
|
120 |
end |
|
121 |
local function auto_handler(event) |
|
122 |
local origin, stanza = event.origin, event.stanza; |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
123 |
if not stanza.attr["type"] == "set" then return false; end |
1471 | 124 |
|
125 |
local user = origin.username; |
|
126 |
local prefs = get_prefs(user); |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
127 |
local auto = stanza:get_child("auto", xmlns_archive); |
1471 | 128 |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
129 |
prefs[false] = auto.attr["save"] ~= nil and auto.attr["save"] == "true" or false; |
1471 | 130 |
set_prefs(user, prefs); |
131 |
||
132 |
return origin.send(st.reply(stanza)); |
|
133 |
end |
|
134 |
||
135 |
-- excerpt from mod_storage_sql2 |
|
136 |
local function get_db() |
|
137 |
local mod_sql = module:require("sql"); |
|
138 |
local params = module:get_option("sql"); |
|
139 |
local engine; |
|
140 |
||
141 |
params = params or { driver = "SQLite3" }; |
|
142 |
if params.driver == "SQLite3" then |
|
143 |
params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); |
|
144 |
end |
|
145 |
||
146 |
assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); |
|
147 |
engine = mod_sql:create_engine(params); |
|
148 |
engine:set_encoding(); |
|
149 |
||
150 |
return engine; |
|
151 |
end |
|
152 |
||
153 |
------------------------------------------------------------ |
|
154 |
-- Collections. In our case there is one conversation with each contact for the whole day for simplicity |
|
155 |
------------------------------------------------------------ |
|
156 |
local function list_stanza_to_query(origin, list_el) |
|
157 |
local sql = "SELECT `with`, `when` / ".. conversation_interval .." as `day`, COUNT(0) FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? "; |
|
158 |
local args = {origin.host, origin.username, archive_store}; |
|
159 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
160 |
local with = list_el.attr["with"]; |
1471 | 161 |
if with ~= nil then |
162 |
sql = sql .. "AND `with` = ? "; |
|
163 |
table.insert(args, jid_bare(with)); |
|
164 |
end |
|
165 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
166 |
local after = list_el.attr["start"]; |
1471 | 167 |
if after ~= nil then |
168 |
sql = sql .. "AND `when` >= ?"; |
|
169 |
table.insert(args, date_parse(after)); |
|
170 |
end |
|
171 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
172 |
local before = list_el.attr["end"]; |
1471 | 173 |
if before ~= nil then |
174 |
sql = sql .. "AND `when` <= ? "; |
|
175 |
table.insert(args, date_parse(before)); |
|
176 |
end |
|
177 |
||
178 |
sql = sql .. "GROUP BY `with`, `when` / ".. conversation_interval .." ORDER BY `when` / ".. conversation_interval .." ASC "; |
|
179 |
||
180 |
local qset = rsm.get(list_el); |
|
181 |
local limit = math.min(qset and qset.max or default_max_items, max_max_items); |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
182 |
sql = sql.."LIMIT ?"; |
1471 | 183 |
table.insert(args, limit); |
184 |
||
185 |
table.insert(args, 1, sql); |
|
186 |
return args; |
|
187 |
end |
|
188 |
local function list_handler(event) |
|
189 |
local db = get_db(); |
|
190 |
local origin, stanza = event.origin, event.stanza; |
|
191 |
local reply = st.reply(stanza); |
|
192 |
||
193 |
local query = list_stanza_to_query(origin, stanza.tags[1]); |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
194 |
local list = reply:tag("list", {xmlns=xmlns_archive}); |
1471 | 195 |
|
196 |
for row in db:select(unpack(query)) do |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
197 |
list:tag("chat", { |
1471 | 198 |
xmlns=xmlns_archive, |
199 |
with=row[1], |
|
200 |
start=date_format(row[2] * conversation_interval), |
|
201 |
version=row[3] |
|
202 |
}):up(); |
|
203 |
end |
|
204 |
||
205 |
origin.send(reply); |
|
206 |
return true; |
|
207 |
end |
|
208 |
||
209 |
------------------------------------------------------------ |
|
210 |
-- Message archive retrieval |
|
211 |
------------------------------------------------------------ |
|
212 |
||
213 |
local function retrieve_handler(event) |
|
214 |
local origin, stanza = event.origin, event.stanza; |
|
215 |
local reply = st.reply(stanza); |
|
216 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
217 |
local retrieve = stanza:get_child("retrieve", xmlns_archive); |
1471 | 218 |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
219 |
local qwith = retrieve.attr["with"]; |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
220 |
local qstart = retrieve.attr["start"]; |
1471 | 221 |
|
222 |
module:log("debug", "Archive query, with %s from %s)", |
|
223 |
qwith or "anyone", qstart or "the dawn of time"); |
|
224 |
||
225 |
if qstart then -- Validate timestamps |
|
226 |
local vstart = (qstart and date_parse(qstart)); |
|
227 |
if (qstart and not vstart) then |
|
228 |
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) |
|
229 |
return true |
|
230 |
end |
|
231 |
qstart = vstart; |
|
232 |
end |
|
233 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
234 |
if qwith then -- Validate the "with" jid |
1471 | 235 |
local pwith = qwith and jid_prep(qwith); |
236 |
if pwith and not qwith then -- it failed prepping |
|
237 |
origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID")) |
|
238 |
return true |
|
239 |
end |
|
240 |
qwith = jid_bare(pwith); |
|
241 |
end |
|
242 |
||
243 |
-- RSM stuff |
|
244 |
local qset = rsm.get(retrieve); |
|
245 |
local qmax = math.min(qset and qset.max or default_max_items, max_max_items); |
|
246 |
local reverse = qset and qset.before or false; |
|
247 |
local before, after = qset and qset.before, qset and qset.after; |
|
248 |
if type(before) ~= "string" then before = nil; end |
|
249 |
||
250 |
-- Load all the data! |
|
251 |
local data, err = archive:find(origin.username, { |
|
252 |
start = qstart; ["end"] = qstart + conversation_interval; |
|
253 |
with = qwith; |
|
254 |
limit = qmax; |
|
255 |
before = before; after = after; |
|
256 |
reverse = reverse; |
|
257 |
total = true; |
|
258 |
}); |
|
259 |
||
260 |
if not data then |
|
261 |
return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); |
|
262 |
end |
|
263 |
local count = err; |
|
264 |
||
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
265 |
local chat = reply:tag("chat", {xmlns=xmlns_archive, with=qwith, start=date_format(qstart), version=count}); |
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
266 |
local first, last; |
1471 | 267 |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
268 |
module:log("debug", "Count "..count); |
1471 | 269 |
for id, item, when in data do |
1477
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
270 |
if not getmetatable(item) == st.stanza_mt then |
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
271 |
item = st.deserialize(item); |
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
272 |
end |
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
273 |
module:log("debug", tostring(item)); |
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
274 |
|
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
275 |
local tag = jid_bare(item.attr["from"]) == jid_bare(origin.full_jid) and "to" or "from"; |
1471 | 276 |
tag = chat:tag(tag, {secs = when - qstart}); |
1477
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
277 |
tag:add_child(item:get_child("body")):up(); |
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
278 |
if not first then first = id; end |
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
279 |
last = id; |
1471 | 280 |
end |
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
281 |
reply:add_child(rsm.generate{ first = first, last = last, count = count }) |
1471 | 282 |
|
283 |
origin.send(reply); |
|
284 |
return true; |
|
285 |
end |
|
286 |
||
287 |
local function not_implemented(event) |
|
288 |
local origin, stanza = event.origin, event.stanza; |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
289 |
local reply = st.reply(stanza):tag("error", {type="cancel"}); |
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
290 |
reply:tag("feature-not-implemented", {xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); |
1471 | 291 |
origin.send(reply); |
292 |
end |
|
293 |
||
294 |
-- Preferences |
|
295 |
module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler); |
|
296 |
module:hook("iq/self/urn:xmpp:archive:auto", auto_handler); |
|
297 |
module:hook("iq/self/urn:xmpp:archive:itemremove", not_implemented); |
|
298 |
module:hook("iq/self/urn:xmpp:archive:sessionremove", not_implemented); |
|
299 |
||
300 |
-- Message Archive Management |
|
301 |
module:hook("iq/self/urn:xmpp:archive:list", list_handler); |
|
302 |
module:hook("iq/self/urn:xmpp:archive:retrieve", retrieve_handler); |
|
303 |
module:hook("iq/self/urn:xmpp:archive:remove", not_implemented); |
|
304 |
||
305 |
-- manual archiving |
|
306 |
module:hook("iq/self/urn:xmpp:archive:save", not_implemented); |
|
307 |
-- replication |
|
308 |
module:hook("iq/self/urn:xmpp:archive:modified", not_implemented); |