comparison mod_mam/mod_mam.lua @ 1324:853a382c9bd6

mod_turncredentials: Advertise the XEP-0215 feature (thanks Gryffus)
author Kim Alvefur <zash@zash.se>
date Fri, 28 Feb 2014 15:36:06 +0100
parents d677d1807bb0
children b21236b6b8d8
comparison
equal deleted inserted replaced
1323:c84ff82658cb 1324:853a382c9bd6
1 -- XEP-0313: Message Archive Management for Prosody 1 -- XEP-0313: Message Archive Management for Prosody
2 -- Copyright (C) 2011-2012 Kim Alvefur 2 -- Copyright (C) 2011-2014 Kim Alvefur
3 -- 3 --
4 -- This file is MIT/X11 licensed. 4 -- This file is MIT/X11 licensed.
5 5
6 local xmlns_mam = "urn:xmpp:mam:tmp"; 6 local xmlns_mam = "urn:xmpp:mam:0" or ":1";
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 rsm = module:require "rsm";
14 local set_prefs, get_prefs = prefs.set, prefs.get; 14 local set_prefs, get_prefs = prefs.set, prefs.get;
15 local prefs_to_stanza, prefs_from_stanza = prefsxml.tostanza, prefsxml.fromstanza; 15 local prefs_to_stanza, prefs_from_stanza = prefsxml.tostanza, prefsxml.fromstanza;
16 local jid_bare = require "util.jid".bare; 16 local jid_bare = require "util.jid".bare;
17 local jid_split = require "util.jid".split; 17 local jid_split = require "util.jid".split;
18 local jid_prep = require "util.jid".prep; 18 local jid_prep = require "util.jid".prep;
19 local dataform = require "util.dataforms".new;
19 local host = module.host; 20 local host = module.host;
20 21
21 local rm_load_roster = require "core.rostermanager".load_roster; 22 local rm_load_roster = require "core.rostermanager".load_roster;
22 23
23 local getmetatable = getmetatable; 24 local getmetatable = getmetatable;
59 end 60 end
60 return origin.send(st.reply(stanza)); 61 return origin.send(st.reply(stanza));
61 end 62 end
62 end); 63 end);
63 64
65 local query_form = dataform {
66 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:mam:0"; };
67 { name = "with"; type = "jid-single"; };
68 { name = "start"; type = "text-single" };
69 { name = "end"; type = "text-single"; };
70 };
71
72 -- Serve form
73 module:hook("iq-get/self/"..xmlns_mam..":query", function(event)
74 local origin, stanza = event.origin, event.stanza;
75 return origin.send(st.reply(stanza):add_child(query_form:form()));
76 end);
77
64 -- Handle archive queries 78 -- Handle archive queries
65 module:hook("iq-get/self/"..xmlns_mam..":query", function(event) 79 module:hook("iq-set/self/"..xmlns_mam..":query", function(event)
66 local origin, stanza = event.origin, event.stanza; 80 local origin, stanza = event.origin, event.stanza;
67 local query = stanza.tags[1]; 81 local query = stanza.tags[1];
68 local qid = query.attr.queryid; 82 local qid = query.attr.queryid;
69 83
70 -- Search query parameters 84 -- Search query parameters
71 local qwith = query:get_child_text("with"); 85 local qwith, qstart, qend;
72 local qstart = query:get_child_text("start"); 86 local form = query:get_child("x", "jabber:x:data");
73 local qend = query:get_child_text("end"); 87 if form then
74 module:log("debug", "Archive query, id %s with %s from %s until %s)", 88 local err;
75 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now"); 89 form, err = query_form:data(form);
90 if err then
91 return origin.send(st.error_reply(stanza, "modify", "bad-request", select(2, next(err))))
92 end
93 qwith, qstart, qend = form["with"], form["start"], form["end"];
94 qwith = qwith and jid_bare(qwith);
95 end
76 96
77 if qstart or qend then -- Validate timestamps 97 if qstart or qend then -- Validate timestamps
78 local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend)) 98 local vstart, vend = (qstart and timestamp_parse(qstart)), (qend and timestamp_parse(qend))
79 if (qstart and not vstart) or (qend and not vend) then 99 if (qstart and not vstart) or (qend and not vend) then
80 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp")) 100 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid timestamp"))
81 return true 101 return true
82 end 102 end
83 qstart, qend = vstart, vend; 103 qstart, qend = vstart, vend;
84 end 104 end
85 105
86 if qwith then -- Validate the 'with' jid 106 module:log("debug", "Archive query, id %s with %s from %s until %s)",
87 local pwith = qwith and jid_prep(qwith); 107 tostring(qid), qwith or "anyone", qstart or "the dawn of time", qend or "now");
88 if pwith and not qwith then -- it failed prepping
89 origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid JID"))
90 return true
91 end
92 qwith = jid_bare(pwith);
93 end
94 108
95 -- RSM stuff 109 -- RSM stuff
96 local qset = rsm.get(query); 110 local qset = rsm.get(query);
97 local qmax = m_min(qset and qset.max or default_max_items, max_max_items); 111 local qmax = m_min(qset and qset.max or default_max_items, max_max_items);
98 local reverse = qset and qset.before or false; 112 local reverse = qset and qset.before or false;
114 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err)); 128 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err));
115 end 129 end
116 local count = err; 130 local count = err;
117 131
118 -- Wrap it in stuff and deliver 132 -- Wrap it in stuff and deliver
119 local first, last; 133 local first_id, last_id, first_time, last_time;
120 for id, item, when in data do 134 for id, item, when in data do
121 local fwd_st = st.message{ to = origin.full_jid } 135 local fwd_st = st.message{ to = origin.full_jid }
122 :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id }) 136 :tag("result", { xmlns = xmlns_mam, queryid = qid, id = id })
123 :tag("forwarded", { xmlns = xmlns_forward }) 137 :tag("forwarded", { xmlns = xmlns_forward })
124 :tag("delay", { xmlns = xmlns_delay, stamp = timestamp(when) }):up(); 138 :tag("delay", { xmlns = xmlns_delay, stamp = timestamp(when) }):up();
127 item = st.deserialize(item); 141 item = st.deserialize(item);
128 end 142 end
129 item.attr.xmlns = "jabber:client"; 143 item.attr.xmlns = "jabber:client";
130 fwd_st:add_child(item); 144 fwd_st:add_child(item);
131 145
132 if not first then first = id; end 146 if not first_id then
133 last = id; 147 first_id = id;
148 first_time = when;
149 end
150 last_id = id;
151 last_time = when;
134 152
135 origin.send(fwd_st); 153 origin.send(fwd_st);
136 end 154 end
137 -- That's all folks! 155 -- That's all folks!
138 module:log("debug", "Archive query %s completed", tostring(qid)); 156 module:log("debug", "Archive query %s completed", tostring(qid));
139 157
140 if reverse then first, last = last, first; end 158 if reverse then
159 first_id, last_id, first_time, last_time =
160 last_id, first_id, last_time, first_time;
161 end
141 return origin.send(st.reply(stanza) 162 return origin.send(st.reply(stanza)
142 :query(xmlns_mam):add_child(rsm.generate { 163 :query(xmlns_mam)
143 first = first, last = last, count = count })); 164 :add_child(query_form:form({ start = timestamp(first_time), ["end"] = timestamp(last_time), with = qwith }))
165 :add_child(rsm.generate {
166 first = first_id, last = last_id, count = count }));
144 end); 167 end);
145 168
146 local function has_in_roster(user, who) 169 local function has_in_roster(user, who)
147 local roster = rm_load_roster(user, host); 170 local roster = rm_load_roster(user, host);
148 module:log("debug", "%s has %s in roster? %s", user, who, roster[who] and "yes" or "no"); 171 module:log("debug", "%s has %s in roster? %s", user, who, roster[who] and "yes" or "no");