Mercurial > prosody-modules
comparison mod_mam/mod_mam.lua @ 701:cc5805f83583
mod_mam: Implement support for Result Set Management in queries.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Fri, 08 Jun 2012 03:13:31 +0200 |
| parents | da33325453fb |
| children | d94ee0848b27 |
comparison
equal
deleted
inserted
replaced
| 700:0c130c45b7c1 | 701:cc5805f83583 |
|---|---|
| 6 local xmlns_mam = "urn:xmpp:mam:tmp"; | 6 local xmlns_mam = "urn:xmpp:mam:tmp"; |
| 7 local xmlns_delay = "urn:xmpp:delay"; | 7 local xmlns_delay = "urn:xmpp:delay"; |
| 8 local xmlns_forward = "urn:xmpp:forward:0"; | 8 local xmlns_forward = "urn:xmpp:forward:0"; |
| 9 | 9 |
| 10 local st = require "util.stanza"; | 10 local st = require "util.stanza"; |
| 11 local rsm = module:require "rsm"; | |
| 11 local jid_bare = require "util.jid".bare; | 12 local jid_bare = require "util.jid".bare; |
| 12 local jid_split = require "util.jid".split; | 13 local jid_split = require "util.jid".split; |
| 13 local host = module.host; | 14 local host = module.host; |
| 14 | 15 |
| 15 local dm_load = require "util.datamanager".load; | 16 local dm_load = require "util.datamanager".load; |
| 60 if stanza.attr.type == "get" then | 61 if stanza.attr.type == "get" then |
| 61 local prefs = get_prefs(user); | 62 local prefs = get_prefs(user); |
| 62 local default = prefs[false]; | 63 local default = prefs[false]; |
| 63 default = default ~= nil and default_attrs[default] or global_default_policy; | 64 default = default ~= nil and default_attrs[default] or global_default_policy; |
| 64 local reply = st.reply(stanza):tag("prefs", { xmlns = xmlns_mam, default = default }) | 65 local reply = st.reply(stanza):tag("prefs", { xmlns = xmlns_mam, default = default }) |
| 65 --module:log("debug", "get_prefs(%q) => %s", user, require"util.serialization".serialize(prefs)); | |
| 66 local always = st.stanza("always"); | 66 local always = st.stanza("always"); |
| 67 local never = st.stanza("never"); | 67 local never = st.stanza("never"); |
| 68 for k,v in pairs(prefs) do | 68 for k,v in pairs(prefs) do |
| 69 if k then | 69 if k then |
| 70 (v and always or never):tag("jid"):text(k):up(); | 70 (v and always or never):tag("jid"):text(k):up(); |
| 95 local jid = rule:get_text(); | 95 local jid = rule:get_text(); |
| 96 prefs[jid] = false; | 96 prefs[jid] = false; |
| 97 end | 97 end |
| 98 end | 98 end |
| 99 | 99 |
| 100 --module:log("debug", "set_prefs(%q, %s)", user, require"util.serialization".serialize(prefs)); | |
| 101 local ok, err = set_prefs(user, prefs); | 100 local ok, err = set_prefs(user, prefs); |
| 102 if not ok then | 101 if not ok then |
| 103 origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); | 102 origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error storing preferences: "..tostring(err))); |
| 104 else | 103 else |
| 105 origin.send(st.reply(stanza)); | 104 origin.send(st.reply(stanza)); |
| 117 | 116 |
| 118 -- Search query parameters | 117 -- Search query parameters |
| 119 local qwith = query:get_child_text("with"); | 118 local qwith = query:get_child_text("with"); |
| 120 local qstart = query:get_child_text("start"); | 119 local qstart = query:get_child_text("start"); |
| 121 local qend = query:get_child_text("end"); | 120 local qend = query:get_child_text("end"); |
| 121 local qset = rsm.get(query); | |
| 122 module:log("debug", "Archive query, id %s with %s from %s until %s)", | 122 module:log("debug", "Archive query, id %s with %s from %s until %s)", |
| 123 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now"); | 123 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now"); |
| 124 | 124 |
| 125 qstart, qend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) | 125 qstart, qend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) |
| 126 | 126 |
| 134 origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error loading archive: "..tostring(err))); | 134 origin.send(st.error_reply(stanza, "cancel", "internal-server-error", "Error loading archive: "..tostring(err))); |
| 135 end | 135 end |
| 136 return true | 136 return true |
| 137 end | 137 end |
| 138 | 138 |
| 139 -- RSM stuff | |
| 140 local qset_matches = not (qset and qset.after); | |
| 141 local first, last, index; | |
| 142 local n = 0; | |
| 143 local start = qset and qset.index or 1; | |
| 144 | |
| 139 module:log("debug", "Loaded %d items, about to filter", #data); | 145 module:log("debug", "Loaded %d items, about to filter", #data); |
| 140 for i=1,#data do | 146 for i=start,#data do |
| 141 local item = data[i]; | 147 local item = data[i]; |
| 142 local when, with, with_bare = item.when, item.with, item.with_bare; | 148 local when, with, with_bare = item.when, item.with, item.with_bare; |
| 143 local ts = item.timestamp; | 149 local ts = item.timestamp; |
| 144 local id = item.id; | 150 local id = item.id; |
| 151 --module:log("debug", "id is %s", id); | |
| 152 | |
| 153 -- RSM pre-send-checking | |
| 154 if qset then | |
| 155 if qset.before == id then | |
| 156 module:log("debug", "End of matching range found"); | |
| 157 qset_matches = false; | |
| 158 break; | |
| 159 end | |
| 160 end | |
| 161 | |
| 145 --module:log("debug", "message with %s at %s", with, when or "???"); | 162 --module:log("debug", "message with %s at %s", with, when or "???"); |
| 146 -- Apply query filter | 163 -- Apply query filter |
| 147 if (not qwith or ((qwith == with) or (qwith == with_bare))) | 164 if (not qwith or ((qwith == with) or (qwith == with_bare))) |
| 148 and (not qstart or when >= qstart) | 165 and (not qstart or when >= qstart) |
| 149 and (not qend or when <= qend) then | 166 and (not qend or when <= qend) |
| 150 -- Optimizable? Do this when archiving? | 167 and (not qset or qset_matches) then |
| 151 --module:log("debug", "sending"); | |
| 152 local fwd_st = st.message{ to = origin.full_jid } | 168 local fwd_st = st.message{ to = origin.full_jid } |
| 153 :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }):up() | 169 :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }):up() |
| 154 :tag("forwarded", { xmlns = xmlns_forward }) | 170 :tag("forwarded", { xmlns = xmlns_forward }) |
| 155 :tag("delay", { xmlns = xmlns_delay, stamp = ts or timestamp(when) }):up(); | 171 :tag("delay", { xmlns = xmlns_delay, stamp = ts or timestamp(when) }):up(); |
| 156 local orig_stanza = st.deserialize(item.stanza); | 172 local orig_stanza = st.deserialize(item.stanza); |
| 157 orig_stanza.attr.xmlns = "jabber:client"; | 173 orig_stanza.attr.xmlns = "jabber:client"; |
| 158 fwd_st:add_child(orig_stanza); | 174 fwd_st:add_child(orig_stanza); |
| 159 origin.send(fwd_st); | 175 origin.send(fwd_st); |
| 160 elseif qend and when > qend then | 176 if not first then |
| 177 index = i; | |
| 178 first = id; | |
| 179 end | |
| 180 last = id; | |
| 181 n = n + 1; | |
| 182 elseif (qend and when > qend) then | |
| 183 module:log("debug", "We have passed into messages more recent than requested"); | |
| 161 break -- We have passed into messages more recent than requested | 184 break -- We have passed into messages more recent than requested |
| 185 end | |
| 186 | |
| 187 -- RSM post-send-checking | |
| 188 if qset then | |
| 189 if qset.after == id then | |
| 190 module:log("debug", "Start of matching range found"); | |
| 191 qset_matches = true; | |
| 192 end | |
| 193 if qset.max and n >= qset.max then | |
| 194 module:log("debug", "Max number of items matched"); | |
| 195 break | |
| 196 end | |
| 162 end | 197 end |
| 163 end | 198 end |
| 164 -- That's all folks! | 199 -- That's all folks! |
| 165 module:log("debug", "Archive query %s completed", tostring(qid)); | 200 module:log("debug", "Archive query %s completed", tostring(qid)); |
| 166 origin.send(st.reply(stanza)); | 201 origin.send(st.reply(stanza):add_child(rsm.generate{first = { index = index; first }, last = last})); |
| 167 return true | 202 return true |
| 168 end | 203 end |
| 169 end); | 204 end); |
| 170 | 205 |
| 171 local function has_in_roster(user, who) | 206 local function has_in_roster(user, who) |
| 219 | 254 |
| 220 local store_user, store_host = jid_split(c2s and orig_from or orig_to); | 255 local store_user, store_host = jid_split(c2s and orig_from or orig_to); |
| 221 local target_jid = c2s and orig_to or orig_from; | 256 local target_jid = c2s and orig_to or orig_from; |
| 222 local target_bare = jid_bare(target_jid); | 257 local target_bare = jid_bare(target_jid); |
| 223 | 258 |
| 224 assert(store_host == host, "This should not happen."); | |
| 225 | |
| 226 if shall_store(store_user, target_bare) then | 259 if shall_store(store_user, target_bare) then |
| 227 module:log("debug", "Archiving stanza: %s", stanza:top_tag()); | 260 module:log("debug", "Archiving stanza: %s", stanza:top_tag()); |
| 228 | 261 |
| 229 local id = uuid(); | 262 local id = uuid(); |
| 230 local when = time_now(); | 263 local when = time_now(); |
| 231 -- And stash it | 264 -- And stash it |
| 232 local ok, err = dm_list_append(store_user, store_host, archive_store, { | 265 local ok, err = dm_list_append(store_user, store_host, archive_store, { |
| 233 -- WARNING This format may change. | 266 -- WARNING This format may change. |
| 234 id = id, | 267 id = id, |
| 235 when = when, -- This might be an UNIX timestamp. Probably. | 268 when = when, |
| 236 timestamp = timestamp(when), -- Textual timestamp. But I'll assume that comparing numbers is faster and less annoying in case of timezones. | |
| 237 with = target_jid, | 269 with = target_jid, |
| 238 with_bare = target_bare, -- Optimization, to avoid loads of jid_bare() calls when filtering. | 270 with_bare = target_bare, -- Optimization, to avoid loads of jid_bare() calls when filtering. |
| 239 stanza = st.preserialize(stanza) | 271 stanza = st.preserialize(stanza) |
| 240 }); | 272 }); |
| 241 --[[ This was dropped from the spec | 273 --[[ This was dropped from the spec |
