Mercurial > prosody-modules
annotate mod_mam_archive/mod_mam_archive.lua @ 6319:04c3273cb81f
mod_auth_cyrus: Add empty 'profile' table to SASL handler objects
This is for compatibility with Prosody's built-in util.sasl objects.
A SASL profile table usually includes methods supported by the backend, which
can be used by SASL mechanism handlers to perform operations (such as testing
the password). It also optionally contains a 'cb' field with channel binding
method handlers.
The Cyrus backend doesn't support channel binding, and doesn't have the same
concept of auth backend methods (it handles all that internally, and Prosody
has no insight or control over it).
Thus, we create an empty profile which informs Prosody that the SASL handler
does not support any of the auth or channel binding methods. Some features
will not work, but they didn't work anyway. This just makes it explicit.
This fixes a traceback in mod_sasl2_fast, which expected SASL handlers to
always contain a 'profile' field.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Thu, 04 Sep 2025 10:14:46 +0100 |
| parents | 8ff308fad9fd |
| children |
| rev | line source |
|---|---|
| 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; | |
|
2706
3e97dae28215
mod_mam_archive: Use util.rsm (fixes #877, depends on recent 0.10+)
Dennis Schridde <devurandom@gmx.net>
parents:
1586
diff
changeset
|
8 local rsm = require "util.rsm"; |
| 1471 | 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 module:add_feature("urn:xmpp:archive:auto"); | |
| 24 module:add_feature("urn:xmpp:archive:manage"); | |
| 25 module:add_feature("urn:xmpp:archive:pref"); | |
| 26 module:add_feature("http://jabber.org/protocol/rsm"); | |
| 27 -- -------------------------------------------------- | |
| 28 | |
| 29 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
|
30 local prefstanza = st.stanza("pref", { xmlns="urn:xmpp:archive" }); |
| 1471 | 31 local default = prefs[false] ~= nil and prefs[false] or global_default_policy; |
| 32 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
33 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
|
34 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
|
35 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
|
36 prefstanza:tag("method", {type="manual", use="concede"}):up(); |
| 1471 | 37 |
| 38 for jid, choice in pairs(prefs) do | |
| 39 if jid then | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
40 prefstanza:tag("item", {jid=jid, otr="prefer", save=choice and "message" or "false" }):up() |
| 1471 | 41 end |
| 42 end | |
| 43 | |
| 44 return prefstanza; | |
| 45 end | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
46 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
|
47 local current_prefs = get_prefs(username); |
| 1471 | 48 |
| 49 -- "default" | "item" | "session" | "method" | |
| 50 for elem in stanza:children() do | |
| 51 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
|
52 current_prefs[false] = elem.attr["save"] == "true"; |
| 1471 | 53 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
|
54 current_prefs[elem.attr["jid"]] = not elem.attr["save"] == "false"; |
| 1471 | 55 elseif elem.name == "session" then |
| 56 module:log("info", "element is not supported: " .. tostring(elem)); | |
| 57 -- local found = false; | |
| 58 -- for child in data:children() do | |
| 59 -- if child.name == elem.name and child.attr["thread"] == elem.attr["thread"] then | |
| 60 -- for k, v in pairs(elem.attr) do | |
| 61 -- child.attr[k] = v; | |
| 62 -- end | |
| 63 -- found = true; | |
| 64 -- break; | |
| 65 -- end | |
| 66 -- end | |
| 67 -- if not found then | |
| 68 -- data:tag(elem.name, elem.attr):up(); | |
| 69 -- end | |
| 70 elseif elem.name == "method" then | |
| 71 module:log("info", "element is not supported: " .. tostring(elem)); | |
| 72 -- local newpref = stanza.tags[1]; -- iq:pref | |
| 73 -- for _, e in ipairs(newpref.tags) do | |
| 74 -- -- if e.name ~= "method" then continue end | |
| 75 -- local found = false; | |
| 76 -- for child in data:children() do | |
| 77 -- if child.name == "method" and child.attr["type"] == e.attr["type"] then | |
| 78 -- child.attr["use"] = e.attr["use"]; | |
| 79 -- found = true; | |
| 80 -- break; | |
| 81 -- end | |
| 82 -- end | |
| 83 -- if not found then | |
| 84 -- data:tag(e.name, e.attr):up(); | |
| 85 -- end | |
| 86 -- end | |
| 87 end | |
| 88 end | |
| 89 end | |
| 90 | |
| 91 ------------------------------------------------------------ | |
| 92 -- Preferences | |
| 93 ------------------------------------------------------------ | |
| 94 local function preferences_handler(event) | |
| 95 local origin, stanza = event.origin, event.stanza; | |
| 96 local user = origin.username; | |
| 97 local reply = st.reply(stanza); | |
| 98 | |
| 99 if stanza.attr.type == "get" then | |
| 100 reply:add_child(prefs_to_stanza(get_prefs(user))); | |
| 101 end | |
| 102 if stanza.attr.type == "set" then | |
| 103 local new_prefs = stanza:get_child("pref", xmlns_archive); | |
| 104 if not new_prefs then return false; end | |
| 105 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
106 local prefs = prefs_from_stanza(stanza, origin.username); |
| 1471 | 107 local ok, err = set_prefs(user, prefs); |
| 108 | |
| 109 if not ok then | |
| 110 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); | |
| 111 end | |
| 112 end | |
| 113 return origin.send(reply); | |
| 114 end | |
| 115 local function auto_handler(event) | |
| 116 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
|
117 if not stanza.attr["type"] == "set" then return false; end |
| 1471 | 118 |
| 119 local user = origin.username; | |
| 120 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
|
121 local auto = stanza:get_child("auto", xmlns_archive); |
| 1471 | 122 |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
123 prefs[false] = auto.attr["save"] ~= nil and auto.attr["save"] == "true" or false; |
| 1471 | 124 set_prefs(user, prefs); |
| 125 | |
| 126 return origin.send(st.reply(stanza)); | |
| 127 end | |
| 128 | |
| 129 -- excerpt from mod_storage_sql2 | |
| 130 local function get_db() | |
| 131 local mod_sql = module:require("sql"); | |
| 132 local params = module:get_option("sql"); | |
| 133 local engine; | |
| 134 | |
| 135 params = params or { driver = "SQLite3" }; | |
| 136 if params.driver == "SQLite3" then | |
| 137 params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); | |
| 138 end | |
| 139 | |
| 140 assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); | |
| 141 engine = mod_sql:create_engine(params); | |
| 142 engine:set_encoding(); | |
| 143 | |
| 144 return engine; | |
| 145 end | |
| 146 | |
| 147 ------------------------------------------------------------ | |
| 148 -- Collections. In our case there is one conversation with each contact for the whole day for simplicity | |
| 149 ------------------------------------------------------------ | |
| 150 local function list_stanza_to_query(origin, list_el) | |
| 151 local sql = "SELECT `with`, `when` / ".. conversation_interval .." as `day`, COUNT(0) FROM `prosodyarchive` WHERE `host`=? AND `user`=? AND `store`=? "; | |
| 152 local args = {origin.host, origin.username, archive_store}; | |
| 153 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
154 local with = list_el.attr["with"]; |
| 1471 | 155 if with ~= nil then |
| 156 sql = sql .. "AND `with` = ? "; | |
| 157 table.insert(args, jid_bare(with)); | |
| 158 end | |
| 159 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
160 local after = list_el.attr["start"]; |
| 1471 | 161 if after ~= nil then |
| 1586 | 162 sql = sql .. "AND `when` >= ? "; |
| 1471 | 163 table.insert(args, date_parse(after)); |
| 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 before = list_el.attr["end"]; |
| 1471 | 167 if before ~= nil then |
| 168 sql = sql .. "AND `when` <= ? "; | |
| 169 table.insert(args, date_parse(before)); | |
| 170 end | |
| 171 | |
| 172 sql = sql .. "GROUP BY `with`, `when` / ".. conversation_interval .." ORDER BY `when` / ".. conversation_interval .." ASC "; | |
| 173 | |
| 174 local qset = rsm.get(list_el); | |
| 175 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
|
176 sql = sql.."LIMIT ?"; |
| 1471 | 177 table.insert(args, limit); |
| 178 | |
| 179 table.insert(args, 1, sql); | |
| 180 return args; | |
| 181 end | |
| 182 local function list_handler(event) | |
| 183 local db = get_db(); | |
| 184 local origin, stanza = event.origin, event.stanza; | |
| 185 local reply = st.reply(stanza); | |
| 186 | |
| 187 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
|
188 local list = reply:tag("list", {xmlns=xmlns_archive}); |
| 1471 | 189 |
| 190 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
|
191 list:tag("chat", { |
| 1471 | 192 xmlns=xmlns_archive, |
| 193 with=row[1], | |
| 194 start=date_format(row[2] * conversation_interval), | |
| 195 version=row[3] | |
| 196 }):up(); | |
| 197 end | |
| 198 | |
| 199 origin.send(reply); | |
| 200 return true; | |
| 201 end | |
| 202 | |
| 203 ------------------------------------------------------------ | |
| 204 -- Message archive retrieval | |
| 205 ------------------------------------------------------------ | |
| 206 | |
| 207 local function retrieve_handler(event) | |
| 208 local origin, stanza = event.origin, event.stanza; | |
| 209 local reply = st.reply(stanza); | |
| 210 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
211 local retrieve = stanza:get_child("retrieve", xmlns_archive); |
| 1471 | 212 |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
213 local qwith = retrieve.attr["with"]; |
|
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
214 local qstart = retrieve.attr["start"]; |
| 1471 | 215 |
| 216 module:log("debug", "Archive query, with %s from %s)", | |
| 217 qwith or "anyone", qstart or "the dawn of time"); | |
| 218 | |
| 219 if qstart then -- Validate timestamps | |
| 220 local vstart = (qstart and date_parse(qstart)); | |
| 221 if (qstart and not vstart) then | |
| 222 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) | |
| 223 return true | |
| 224 end | |
| 225 qstart = vstart; | |
| 226 end | |
| 227 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
228 if qwith then -- Validate the "with" jid |
| 1471 | 229 local pwith = qwith and jid_prep(qwith); |
| 230 if pwith and not qwith then -- it failed prepping | |
| 231 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID")) | |
| 232 return true | |
| 233 end | |
| 234 qwith = jid_bare(pwith); | |
| 235 end | |
| 236 | |
| 237 -- RSM stuff | |
| 238 local qset = rsm.get(retrieve); | |
| 239 local qmax = math.min(qset and qset.max or default_max_items, max_max_items); | |
| 240 local reverse = qset and qset.before or false; | |
| 241 local before, after = qset and qset.before, qset and qset.after; | |
| 242 if type(before) ~= "string" then before = nil; end | |
| 243 | |
| 244 -- Load all the data! | |
| 245 local data, err = archive:find(origin.username, { | |
| 246 start = qstart; ["end"] = qstart + conversation_interval; | |
| 247 with = qwith; | |
| 248 limit = qmax; | |
| 249 before = before; after = after; | |
| 250 reverse = reverse; | |
| 1523 | 251 total = true; |
| 1471 | 252 }); |
| 253 | |
| 254 if not data then | |
| 255 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); | |
| 256 end | |
| 257 local count = err; | |
| 258 | |
|
1476
08ca6dd36e39
mod_mam_archive: Fixing issues noted in code review for 153df603f73d3b69c434f2790cff0270de14bb75
syn@syn.im
parents:
1471
diff
changeset
|
259 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
|
260 local first, last; |
| 1523 | 261 |
| 262 module:log("debug", "Count "..count); | |
| 1471 | 263 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
|
264 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
|
265 item = st.deserialize(item); |
|
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
266 end |
|
db870913e1cf
mod_mam_archive: Doing stanza deserialization after mod_storage the right way
syn@syn.im
parents:
1476
diff
changeset
|
267 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
|
268 |
|
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
269 local tag = jid_bare(item.attr["from"]) == jid_bare(origin.full_jid) and "to" or "from"; |
| 1471 | 270 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
|
271 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
|
272 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
|
273 last = id; |
| 1471 | 274 end |
|
1498
e82592ed744b
mod_mam_archive: Applying @vstakhov 's patch (https://gist.github.com/vstakhov/306ea813a38021dcf3d4).
syn@syn.im
parents:
1477
diff
changeset
|
275 reply:add_child(rsm.generate{ first = first, last = last, count = count }) |
| 1471 | 276 |
| 277 origin.send(reply); | |
| 278 return true; | |
| 279 end | |
| 280 | |
| 281 local function not_implemented(event) | |
| 282 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
|
283 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
|
284 reply:tag("feature-not-implemented", {xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); |
| 1471 | 285 origin.send(reply); |
| 286 end | |
| 287 | |
| 288 -- Preferences | |
| 289 module:hook("iq/self/urn:xmpp:archive:pref", preferences_handler); | |
| 290 module:hook("iq/self/urn:xmpp:archive:auto", auto_handler); | |
| 291 module:hook("iq/self/urn:xmpp:archive:itemremove", not_implemented); | |
| 292 module:hook("iq/self/urn:xmpp:archive:sessionremove", not_implemented); | |
| 293 | |
| 294 -- Message Archive Management | |
| 295 module:hook("iq/self/urn:xmpp:archive:list", list_handler); | |
| 296 module:hook("iq/self/urn:xmpp:archive:retrieve", retrieve_handler); | |
| 297 module:hook("iq/self/urn:xmpp:archive:remove", not_implemented); | |
| 298 | |
| 299 -- manual archiving | |
| 300 module:hook("iq/self/urn:xmpp:archive:save", not_implemented); | |
| 301 -- replication | |
| 302 module:hook("iq/self/urn:xmpp:archive:modified", not_implemented); |
