Hi folks

I need something like mod_muc_log but I need to log to Postgres, so I wrote a
patch for mod_muc_log that does this by using the newer storage API.

In order to do this, I copied liberally from mod_muc_mam.

Some changes made:

- Use newer module storage API so that we can also store in SQL
- Adhere to config option `muc_log_all_rooms` (also used by mod_muc_mam)
- Add affiliation information in the logged stanza
- Remove code that set (and then removed) an "alreadyJoined" dummy element

I'm not really sure whether I should be using the `archive` store (like
mod_muc_mam does). Is this OK, or is it better to use a different store, or to
create a new one?

I removed the "alreadyJoined" element code since it doesn't look to me like
it's actually doing anything useful, so I assumed it's leftovers from previous
refactoring. Is that so, or why was that code there?

I don't think my changes are entirely backwards compatible because the stanza
being logged is no longer wrapped with `<stanza time=...>`. I'm not sure how to
make it backwards compatible aside from having an `if` statement and then
logging data the old way if internal storage is used.

Alternatively, I could publish this as a new module instead of patching
mod_muc_log.

I look forward to your feedback.

Thanks
JC

-- 
You received this message because you are subscribed to the Google Groups 
"prosody-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/prosody-dev.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/prosody-dev/20190507144510.GA32529%40opkode.com.
For more options, visit https://groups.google.com/d/optout.
--- src/prosody-modules/mod_muc_log/mod_muc_log.lua	2019-05-07 14:03:06.836030801 +0200
+++ modules/mod_muc_archive/mod_muc_archive.lua	2019-05-07 16:28:37.276266654 +0200
@@ -3,18 +3,40 @@
 -- Copyright (C) 2009-2013 Matthew Wild
 -- Copyright (C) 2013 Kim Alvefur
 -- Copyright (C) 2013 Marco Cirillo
+-- Copyright (C) 2019 JC Brand
 
 local hosts = prosody.hosts;
 local tostring = tostring;
+local st = require "util.stanza";
 local split_jid = require "util.jid".split;
-local datamanager = require"core.storagemanager".olddm;
-local data_load, data_store = datamanager.load, datamanager.store;
+local jid_bare = require "util.jid".bare;
+local time_now = os.time;
+
 local datastore = "muc_log";
 local muc_form_config_option = "muc#roomconfig_enablelogging"
 
+local log_all_rooms = module:get_option_boolean("muc_log_all_rooms", false);
 local log_by_default = module:get_option_boolean("muc_log_by_default", false);
 local log_presences = module:get_option_boolean("muc_log_presences", true);
 
+local archive_store = "muc_logging_archive";
+local archive = module:open_store(archive_store, "archive");
+
+local xmlns_muc_user = "http://jabber.org/protocol/muc#user";;
+
+if archive.name == "null" or not archive.find then
+	if not archive.find then
+		module:log("error", "Attempt to open archive storage returned a driver without archive API support");
+		module:log("error", "mod_%s does not support archiving",
+			archive._provided_by or archive.name and "storage_"..archive.name.."(?)" or "<unknown>");
+	else
+		module:log("error", "Attempt to open archive storage returned null driver");
+	end
+	module:log("info", "See https://prosody.im/doc/storage and https://prosody.im/doc/archiving for more information");
+	return false;
+end
+
+
 -- Module Definitions
 
 local function get_room_from_jid(jid)
@@ -35,6 +57,12 @@
 end
 
 local function logging_enabled(room)
+	if log_all_rooms then
+		return true;
+	end
+	if room._data.hidden then -- do not log data of private rooms
+		return false;
+	end
 	local enabled = room._data.logging;
 	if enabled == nil then
 		return log_by_default;
@@ -42,6 +70,7 @@
 	return enabled;
 end
 
+
 function log_if_needed(event)
 	local stanza = event.stanza;
 
@@ -51,112 +80,90 @@
 	then
 		local node, host = split_jid(stanza.attr.to);
 		if node and host then
-			local bare = node .. "@" .. host;
-			if get_room_from_jid(bare) then
-				local room = get_room_from_jid(bare)
-
-				local today = os.date("!%y%m%d");
-				local now = os.date("!%H:%M:%S");
-
-				local muc_to = nil
-				local muc_from = nil;
-				local already_joined = false;
-
-				if room._data.hidden then -- do not log any data of private rooms
-					return;
-				end
-				if not logging_enabled(room) then -- do not log where logging is not enabled
-					return;
-				end
-
-				if stanza.name == "presence" and stanza.attr.type == nil then
-					muc_from = stanza.attr.to;
-					if room._occupants and room._occupants[stanza.attr.to] then
-						already_joined = true;
-						stanza:tag("alreadyJoined"):text("true");
-					end
-				elseif stanza.name == "iq" and stanza.attr.type == "set" then -- kick, to is the room, from is the admin, nick who is kicked is attr of iq->query->item
-					if stanza.tags[1] and stanza.tags[1].name == "query" then
-						local tmp = stanza.tags[1];
-						if tmp.tags[1] ~= nil and tmp.tags[1].name == "item" and tmp.tags[1].attr.nick then
-							tmp = tmp.tags[1];
-							for jid, nick in pairs(room._jid_nick) do
-								if nick == stanza.attr.to .. "/" .. tmp.attr.nick then
-									muc_to = nick;
-									break;
-								end
+			local room = get_room_from_jid(node.."@"..host)
+			if not room then return end
+			-- Policy check
+			if not logging_enabled(room) then return end
+
+			local muc_to = nil
+			local muc_from = nil;
+
+			if stanza.name == "presence" and stanza.attr.type == nil then
+				muc_from = stanza.attr.to;
+			elseif stanza.name == "iq" and stanza.attr.type == "set" then -- kick, to is the room, from is the admin, nick who is kicked is attr of iq->query->item
+				if stanza.tags[1] and stanza.tags[1].name == "query" then
+					local tmp = stanza.tags[1];
+					if tmp.tags[1] ~= nil and tmp.tags[1].name == "item" and tmp.tags[1].attr.nick then
+						tmp = tmp.tags[1];
+						for jid, nick in pairs(room._jid_nick) do
+							if nick == stanza.attr.to .. "/" .. tmp.attr.nick then
+								muc_to = nick;
+								break;
 							end
 						end
 					end
-				else
-					for jid, nick in pairs(room._jid_nick) do
-						if jid == stanza.attr.from then
-							muc_from = nick;
-							break;
-						end
+				end
+			else
+				for jid, nick in pairs(room._jid_nick) do
+					if jid == stanza.attr.from then
+						muc_from = nick;
+						break;
 					end
 				end
+			end
 
-				if (muc_from or muc_to) then
-					local data = data_load(node, host, datastore .. "/" .. today);
-					local realFrom = stanza.attr.from;
-					local realTo = stanza.attr.to;
-
-					if data == nil then
-						data = {};
-					end
+			if (muc_from or muc_to) then
+				local stored_stanza = st.clone(stanza);
+				stored_stanza.attr.from = muc_from;
+				stored_stanza.attr.to = muc_to;
+
+				if stanza.name == "message" then
+					local actor = jid_bare(room._occupants[muc_from].jid);
+					local affiliation = room:get_affiliation(actor) or "none";
+					local role = room:get_role(actor) or room:get_default_role(affiliation);
+					stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user })
+						:tag("item", { affiliation = affiliation; role = role; jid = actor }));
+				end
 
-					stanza.attr.from = muc_from;
-					stanza.attr.to = muc_to;
-					data[#data + 1] = "<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n";
-					stanza.attr.from = realFrom;
-					stanza.attr.to = realTo;
-					if already_joined == true then
-						if stanza[#stanza].name == "alreadyJoined" then  -- normaly the faked element should be the last, remove it when it is the last
-							stanza[#stanza] = nil;
-						else
-							for i = 1, #stanza, 1 do
-								if stanza[i].name == "alreadyJoined" then  -- remove the faked element
-									stanza[i] = nil;
-									break;
-								end
-							end
-						end
-					end
-					datamanager.getpath(node, host, datastore, nil, true); -- create the datastore dir
-					data_store(node, host, datastore .. "/" .. today, data);
+				local with = stanza.name
+				if stanza.attr.type then
+					with = with .. "<" .. stanza.attr.type
 				end
+				archive:append(node, nil, stored_stanza, time_now(), with);
 			end
 		end
 	end
 end
 
-module:hook("muc-config-form", function(event)
-	local room, form = event.room, event.form;
-	table.insert(form,
-	{
-		name = muc_form_config_option,
-		type = "boolean",
-		label = "Enable Logging?",
-		value = logging_enabled(room),
-	}
-	);
-end);
-
-module:hook("muc-config-submitted", function(event)
-	local room, fields, changed = event.room, event.fields, event.changed;
-	local new = fields[muc_form_config_option];
-	if new ~= room._data.logging then
-		room._data.logging = new;
-		if type(changed) == "table" then
-			changed[muc_form_config_option] = true;
-		else
-			event.changed = true;
+if not log_all_rooms then
+	module:hook("muc-config-form", function(event)
+		local room, form = event.room, event.form;
+		table.insert(form,
+		{
+			name = muc_form_config_option,
+			type = "boolean",
+			label = "Enable Logging?",
+			value = logging_enabled(room),
+		}
+		);
+	end);
+
+	module:hook("muc-config-submitted", function(event)
+		local room, fields, changed = event.room, event.fields, event.changed;
+		local new = fields[muc_form_config_option];
+		if new ~= room._data.logging then
+			room._data.logging = new;
+			if type(changed) == "table" then
+				changed[muc_form_config_option] = true;
+			else
+				event.changed = true;
+			end
 		end
-	end
-end);
+	end);
+end
 
 module:hook("message/bare", log_if_needed, 1);
+
 if log_presences then
 	module:hook("iq/bare", log_if_needed, 1);
 	module:hook("presence/full", log_if_needed, 1);

Attachment: signature.asc
Description: PGP signature

Reply via email to