Changeset

10758:d0e6d5bc7ea2

Merge with upstream trunk
author Matthew Wild <mwild1@gmail.com>
date Thu, 23 Apr 2020 13:53:18 +0100
parents 10757:9dec7cddb40b (current diff) 10752:930f38939f1e (diff)
children 10759:817f1d6b499e
files
diffstat 11 files changed, 203 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/doc/doap.xml	Thu Apr 23 13:52:19 2020 +0100
+++ b/doc/doap.xml	Thu Apr 23 13:53:18 2020 +0100
@@ -431,6 +431,14 @@
     </implements>
     <implements>
       <xmpp:SupportedXep>
+        <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0249.html"/>
+        <xmpp:version>1.2</xmpp:version>
+        <xmpp:since>0.12</xmpp:since>
+        <xmpp:note>mod_csi_simple</xmpp:note>
+      </xmpp:SupportedXep>
+    </implements>
+    <implements>
+      <xmpp:SupportedXep>
         <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0280.html"/>
         <xmpp:version>0.12.1</xmpp:version>
         <xmpp:status>complete</xmpp:status>
--- a/plugins/mod_csi_simple.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/plugins/mod_csi_simple.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -48,6 +48,9 @@
 		if stanza:get_child("encryption", "urn:xmpp:eme:0") then
 			return true;
 		end
+		if stanza:get_child("x", "jabber:x:conference") or stanza:find("{http://jabber.org/protocol/muc#user}x/invite") then
+			return true;
+		end
 		for important in important_payloads do
 			if stanza:find(important) then
 				return true;
--- a/plugins/mod_lastactivity.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/plugins/mod_lastactivity.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -30,7 +30,7 @@
 	if not stanza.attr.to or is_contact_subscribed(username, module.host, jid_bare(stanza.attr.from)) then
 		local seconds, text = "0", "";
 		if map[username] then
-			seconds = tostring(os.difftime(os.time(), map[username].t));
+			seconds = string.format("%d", os.difftime(os.time(), map[username].t));
 			text = map[username].s;
 		end
 		origin.send(st.reply(stanza):tag('query', {xmlns='jabber:iq:last', seconds=seconds}):text(text));
--- a/plugins/mod_mam/mod_mam.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/plugins/mod_mam/mod_mam.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -263,11 +263,65 @@
 	return stanza;
 end
 
+local function should_store(stanza) --> boolean, reason: string
+	local st_type = stanza.attr.type or "normal";
+	-- FIXME pass direction of stanza and use that along with bare/full JID addressing
+	-- for more accurate MUC / type=groupchat check
+
+	if st_type == "headline" then
+		-- Headline messages are ephemeral by definition
+		return false, "headline";
+	end
+	if st_type == "error" then
+		return true, "bounce";
+	end
+	if st_type == "groupchat" then
+		-- MUC messages always go to the full JID, usually archived by the MUC
+		return false, "groupchat";
+	end
+	if stanza:get_child("no-store", "urn:xmpp:hints")
+	or stanza:get_child("no-permanent-store", "urn:xmpp:hints") then
+		-- XXX Experimental XEP
+		return false, "hint";
+	end
+	if stanza:get_child("store", "urn:xmpp:hints") then
+		return true, "hint";
+	end
+	if stanza:get_child("body") then
+		return true, "body";
+	end
+	if stanza:get_child("subject") then
+		-- XXX Who would send a message with a subject but without a body?
+		return true, "subject";
+	end
+	if stanza:get_child("encryption", "urn:xmpp:eme:0") then
+		-- Since we can't know what an encrypted message contains, we assume it's important
+		-- XXX Experimental XEP
+		return true, "encrypted";
+	end
+	if stanza:get_child(nil, "urn:xmpp:receipts") then
+		-- If it's important enough to ask for a receipt then it's important enough to archive
+		-- and the same applies to the receipt
+		return true, "receipt";
+	end
+	if stanza:get_child(nil, "urn:xmpp:chat-markers:0") then
+		-- XXX Experimental XEP
+		return true, "marker";
+	end
+	if stanza:get_child("x", "jabber:x:conference")
+	or stanza:find("{http://jabber.org/protocol/muc#user}x/invite") then
+		return true, "invite";
+	end
+
+	 -- The IM-NG thing to do here would be to return `not st_to_full`
+	 -- One day ...
+	return false, "default";
+end
+
 -- Handle messages
 local function message_handler(event, c2s)
 	local origin, stanza = event.origin, event.stanza;
 	local log = c2s and origin.log or module._log;
-	local orig_type = stanza.attr.type or "normal";
 	local orig_from = stanza.attr.from;
 	local orig_to = stanza.attr.to or orig_from;
 	-- Stanza without 'to' are treated as if it was to their own bare jid
@@ -280,21 +334,12 @@
 	-- Filter out <stanza-id> that claim to be from us
 	event.stanza = strip_stanza_id(stanza, store_user);
 
-	-- We store chat messages or normal messages that have a body
-	if not(orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) ) then
-		log("debug", "Not archiving stanza: %s (type)", stanza:top_tag());
+	local should, why = should_store(stanza);
+	if not should then
+		log("debug", "Not archiving stanza: %s (%s)", stanza:top_tag(), why);
 		return;
 	end
 
-	-- or if hints suggest we shouldn't
-	if not stanza:get_child("store", "urn:xmpp:hints") then -- No hint telling us we should store
-		if stanza:get_child("no-permanent-store", "urn:xmpp:hints")
-			or stanza:get_child("no-store", "urn:xmpp:hints") then -- Hint telling us we should NOT store
-			log("debug", "Not archiving stanza: %s (hint)", stanza:top_tag());
-			return;
-		end
-	end
-
 	local clone_for_storage;
 	if not strip_tags:empty() then
 		clone_for_storage = st.clone(stanza);
@@ -315,7 +360,7 @@
 
 	-- Check with the users preferences
 	if shall_store(store_user, with) then
-		log("debug", "Archiving stanza: %s", stanza:top_tag());
+		log("debug", "Archiving stanza: %s (%s)", stanza:top_tag(), why);
 
 		-- And stash it
 		local time = time_now();
--- a/plugins/mod_uptime.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/plugins/mod_uptime.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -16,7 +16,7 @@
 
 module:hook("iq-get/host/jabber:iq:last:query", function(event)
 	local origin, stanza = event.origin, event.stanza;
-	origin.send(st.reply(stanza):tag("query", {xmlns = "jabber:iq:last", seconds = tostring(os.difftime(os.time(), start_time))}));
+	origin.send(st.reply(stanza):tag("query", {xmlns = "jabber:iq:last", seconds = tostring(("%d"):format(os.difftime(os.time(), start_time)))}));
 	return true;
 end);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/lastactivity.scs	Thu Apr 23 13:53:18 2020 +0100
@@ -0,0 +1,45 @@
+# XEP-0012: Last Activity / mod_lastactivity
+
+[Client] Romeo
+	jid: romeo@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<presence>
+		<status>Hello</status>
+	</presence>
+
+Romeo receives:
+	<presence from="${Romeo's full JID}">
+		<status>Hello</status>
+	</presence>
+
+Romeo sends:
+	<presence type="unavailable">
+		<status>Goodbye</status>
+	</presence>
+
+Romeo receives:
+	<presence from="${Romeo's full JID}" type="unavailable">
+		<status>Goodbye</status>
+	</presence>
+
+# mod_lastlog saves time + status message from the last unavailable presence
+
+Romeo sends:
+	<iq id='a' type='get'>
+		<query xmlns='jabber:iq:last'/>
+	</iq>
+
+Romeo receives:
+	<iq type='result' id='a'>
+		<query xmlns='jabber:iq:last' seconds='0'>Goodbye</query>
+	</iq>
+
+Romeo disconnects
+
+# recording ended on 2020-04-20T14:39:47Z
--- a/spec/scansion/prosody.cfg.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/spec/scansion/prosody.cfg.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -1,5 +1,16 @@
 --luacheck: ignore
 
+-- Mock time functions to simplify tests
+function _G.os.time()
+	return 1219439344;
+end
+package.preload["util.time"] = function ()
+	return {
+		now = function () return 1219439344.1; end;
+		monotonic = function () return 0.1; end;
+	}
+end
+
 admins = { "admin@localhost" }
 
 use_libevent = true
@@ -48,6 +59,7 @@
 		--"motd"; -- Send a message to users when they log in
 		--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
 		--"proxy65"; -- Enables a file transfer proxy service which clients behind NAT can use
+		"lastactivity";
 
 	-- Useful for testing
 		--"scansion_record"; -- Records things that happen in scansion test case format
@@ -84,6 +96,8 @@
 
 VirtualHost "localhost"
 
+hide_os_type = true -- absense tested for in version.scs
+
 Component "conference.localhost" "muc"
 	storage = "memory"
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/uptime.scs	Thu Apr 23 13:53:18 2020 +0100
@@ -0,0 +1,21 @@
+# XEP-0012: Last Activity / mod_uptime
+
+[Client] Romeo
+	jid: romeo@localhost
+	password: password
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<iq id='a' type='get' to='localhost'>
+		<query xmlns='jabber:iq:last'/>
+	</iq>
+
+Romeo receives:
+	<iq type='result' id='a' from='localhost'>
+		<query xmlns='jabber:iq:last' seconds='0'/>
+	</iq>
+
+Romeo disconnects
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spec/scansion/version.scs	Thu Apr 23 13:53:18 2020 +0100
@@ -0,0 +1,27 @@
+# XEP-0092: Software Version / mod_version
+
+[Client] Romeo
+	password: password
+	jid: romeo@localhost/dfaZpuxV
+
+-----
+
+Romeo connects
+
+Romeo sends:
+	<iq id='lx2' to='localhost' type='get'>
+		<query xmlns='jabber:iq:version'/>
+	</iq>
+
+# Version string would vary so we can't do an exact match atm
+# Inclusion of <os/> is disabled in the config, it should be absent
+Romeo receives:
+	<iq id='lx2' from='localhost' type='result'>
+		<query xmlns='jabber:iq:version' scansion:strict='true'>
+			<name>Prosody</name>
+			<version scansion:strict='false'/>
+		</query>
+	</iq>
+
+
+Romeo disconnects
--- a/spec/util_hashes_spec.lua	Thu Apr 23 13:52:19 2020 +0100
+++ b/spec/util_hashes_spec.lua	Thu Apr 23 13:53:18 2020 +0100
@@ -4,34 +4,52 @@
 
 -- Also see spec for util.hmac where HMAC test cases reside
 
-describe("PBKDF2-SHA1", function ()
+describe("PBKDF2-HMAC-SHA1", function ()
 	it("test vector 1", function ()
 		local P = "password"
 		local S = "salt"
 		local c = 1
 		local DK = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
-		assert.equal(DK, hex.to(hashes.scram_Hi_sha1(P, S, c)));
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha1(P, S, c)));
 	end);
 	it("test vector 2", function ()
 		local P = "password"
 		local S = "salt"
 		local c = 2
 		local DK = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
-		assert.equal(DK, hex.to(hashes.scram_Hi_sha1(P, S, c)));
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha1(P, S, c)));
 	end);
 	it("test vector 3", function ()
 		local P = "password"
 		local S = "salt"
 		local c = 4096
 		local DK = "4b007901b765489abead49d926f721d065a429c1";
-		assert.equal(DK, hex.to(hashes.scram_Hi_sha1(P, S, c)));
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha1(P, S, c)));
 	end);
 	it("test vector 4 #SLOW", function ()
 		local P = "password"
 		local S = "salt"
 		local c = 16777216
 		local DK = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
-		assert.equal(DK, hex.to(hashes.scram_Hi_sha1(P, S, c)));
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha1(P, S, c)));
 	end);
 end);
 
+describe("PBKDF2-HMAC-SHA256", function ()
+	it("test vector 1", function ()
+		local P = "password";
+		local S = "salt";
+		local c = 1
+		local DK = "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b";
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha256(P, S, c)));
+	end);
+	it("test vector 2", function ()
+		local P = "password";
+		local S = "salt";
+		local c = 2
+		local DK = "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43";
+		assert.equal(DK, hex.to(hashes.pbkdf2_hmac_sha256(P, S, c)));
+	end);
+end);
+
+
--- a/util-src/hashes.c	Thu Apr 23 13:52:19 2020 +0100
+++ b/util-src/hashes.c	Thu Apr 23 13:53:18 2020 +0100
@@ -129,7 +129,7 @@
 		return luaL_error(L, "PKCS5_PBKDF2_HMAC() failed");
 	}
 
-	lua_pushlstring(L, (char *)out, SHA_DIGEST_LENGTH);
+	lua_pushlstring(L, (char *)out, SHA256_DIGEST_LENGTH);
 	return 1;
 }