diff --git a/resources/file-sharing.yaml b/resources/file-sharing.yaml index 233264e953..14ec573686 100644 --- a/resources/file-sharing.yaml +++ b/resources/file-sharing.yaml @@ -72,7 +72,7 @@ paths: properties: metadata: type: string - description: The metadata of the document in JSON format. + description: The metadata of the document in JSON format. Must conform to FileMetadata schema. example: '{"conferenceFullName":"myroomname@conference.tenant.jitsi-meet.example.com","timestamp":1741017572040,"fileSize":1042157,"fileId":"e393a7e5-e790-4f43-836e-d27238201904"}' file: type: string @@ -162,6 +162,34 @@ paths: description: OK components: schemas: + FileMetadata: + type: object + required: + - fileId + - conferenceFullName + - timestamp + - fileSize + properties: + fileId: + type: string + format: uuid + description: Client-generated unique identifier for the file + example: e393a7e5-e790-4f43-836e-d27238201904 + conferenceFullName: + type: string + description: Full name of the conference/meeting room + example: myroomname@conference.tenant.jitsi-meet.example.com + timestamp: + type: integer + format: int64 + description: Upload timestamp in milliseconds since epoch + example: 1741017572040 + fileSize: + type: integer + format: int64 + description: Size of the file in bytes + example: 1042157 + description: Metadata structure that must be sent as JSON string in the metadata field Payload: type: object properties: diff --git a/resources/prosody-plugins/mod_filesharing_component.lua b/resources/prosody-plugins/mod_filesharing_component.lua index 588d6d75c4..6268c4fe52 100644 --- a/resources/prosody-plugins/mod_filesharing_component.lua +++ b/resources/prosody-plugins/mod_filesharing_component.lua @@ -13,6 +13,11 @@ local JSON_TYPE_REMOVE_FILE = 'remove'; local JSON_TYPE_LIST_FILES = 'list'; local NICK_NS = 'http://jabber.org/protocol/nick'; +-- this is the main virtual host of the main prosody that this vnode serves +local main_domain = module:get_option_string('main_domain'); +-- only the visitor prosody has main_domain setting +local is_visitor_prosody = main_domain ~= nil; + local muc_component_host = module:get_option_string('muc_component'); if muc_component_host == nil then module:log('error', 'No muc_component specified. No muc to operate on!'); @@ -94,34 +99,13 @@ function on_message(event) end msg_obj.conferenceFullName = internal_room_jid_match_rewrite(room.jid); - if not room.jitsi_shared_files then - room.jitsi_shared_files = {}; - end - - room.jitsi_shared_files[msg_obj.fileId] = msg_obj; - - local json_msg, error = json.encode({ - type = FILE_SHARING_IDENTITY_TYPE, - event = JSON_TYPE_ADD_FILE, - file = msg_obj + module:context(muc_domain_base):fire_event('jitsi-filesharing-add', { + room = room; file = msg_obj; actor = occupant.nick; }); - if not json_msg then - module:log('error', 'skip sending add request room:%s error:%s', room.jid, error); - return false - end - - local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }) - :text(json_msg):up(); - - -- send add file to all occupants except jicofo and sender - for _, room_occupant in room:each_occupant() do - if not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= occupant.nick then - local to_send = st.clone(stanza); - to_send.attr.to = room_occupant.jid; - module:send(to_send); - end - end + module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', { + room = room; + }); return true; elseif message.attr.type == JSON_TYPE_REMOVE_FILE then @@ -130,34 +114,13 @@ function on_message(event) return true; end - if not room.jitsi_shared_files then - return; - end - - room.jitsi_shared_files[message.attr.fileId] = nil; - - local json_msg, error = json.encode({ - type = FILE_SHARING_IDENTITY_TYPE, - event = JSON_TYPE_REMOVE_FILE, - fileId = message.attr.fileId + module:context(muc_domain_base):fire_event('jitsi-filesharing-remove', { + room = room; id = message.attr.fileId; actor = occupant.nick; }); - if not json_msg then - module:log('error', 'skip sending remove request room:%s error:%s', room.jid, error); - return false - end - - local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }) - :text(json_msg):up(); - - -- send remove file to all occupants except jicofo and sender - for _, room_occupant in room:each_occupant() do - if not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= occupant.nick then - local to_send = st.clone(stanza); - to_send.attr.to = room_occupant.jid; - module:send(to_send); - end - end + module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', { + room = room; + }); return true; else @@ -202,7 +165,81 @@ end); module:hook('message/host', on_message); process_host_module(muc_domain_base, function(host_module, host) - module:context(host_module.host):fire_event('jitsi-add-identity', { + module:context(muc_domain_base):fire_event('jitsi-add-identity', { name = FILE_SHARING_IDENTITY_TYPE; host = module.host; }); + module:context(muc_domain_base):hook('jitsi-filesharing-add', function(event) + local actor, file, room = event.actor, event.file, event.room; + + if not room.jitsi_shared_files then + room.jitsi_shared_files = {}; + end + + room.jitsi_shared_files[file.fileId] = file; + + local json_msg, error = json.encode({ + type = FILE_SHARING_IDENTITY_TYPE, + event = JSON_TYPE_ADD_FILE, + file = file + }); + + if not json_msg then + module:log('error', 'skip sending add request room:%s error:%s', room.jid, error); + return false + end + + local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }) + :text(json_msg):up(); + + -- send add file to all occupants except jicofo and sender + -- if this is visitor prosody send it only to visitors + for _, room_occupant in room:each_occupant() do + local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor; + if is_visitor_prosody then + send_event = room_occupant.role == 'visitor'; + end + if send_event then + local to_send = st.clone(stanza); + to_send.attr.to = room_occupant.jid; + module:send(to_send); + end + end + end); + module:context(muc_domain_base):hook('jitsi-filesharing-remove', function(event) + local actor, id, room = event.actor, event.id, event.room; + + if not room.jitsi_shared_files then + return; + end + + room.jitsi_shared_files[id] = nil; + + local json_msg, error = json.encode({ + type = FILE_SHARING_IDENTITY_TYPE, + event = JSON_TYPE_REMOVE_FILE, + fileId = id + }); + + if not json_msg then + module:log('error', 'skip sending remove request room:%s error:%s', room.jid, error); + return false + end + + local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' }) + :text(json_msg):up(); + + -- send remove file to all occupants except jicofo and sender + -- if this is visitor prosody send it only to visitors + for _, room_occupant in room:each_occupant() do + local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor; + if is_visitor_prosody then + send_event = room_occupant.role == 'visitor'; + end + if send_event then + local to_send = st.clone(stanza); + to_send.attr.to = room_occupant.jid; + module:send(to_send); + end + end + end); end); diff --git a/resources/prosody-plugins/mod_fmuc.lua b/resources/prosody-plugins/mod_fmuc.lua index 9753c3ef21..d2c9228c6e 100644 --- a/resources/prosody-plugins/mod_fmuc.lua +++ b/resources/prosody-plugins/mod_fmuc.lua @@ -16,6 +16,7 @@ local new_id = require 'util.id'.medium; local filters = require 'util.filters'; local array = require 'util.array'; local set = require 'util.set'; +local json = require 'cjson.safe'; local util = module:require 'util'; local is_admin = util.is_admin; @@ -27,6 +28,7 @@ local get_focus_occupant = util.get_focus_occupant; local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite; local presence_check_status = util.presence_check_status; local respond_iq_result = util.respond_iq_result; +local table_compare = util.table_compare; local PARTICIPANT_PROP_RAISE_HAND = 'jitsi_participant_raisedHand'; local PARTICIPANT_PROP_REQUEST_TRANSCRIPTION = 'jitsi_participant_requestingTranscription'; @@ -672,6 +674,29 @@ local function iq_from_main_handler(event) end end + local files = node:get_child('files'); + if files then + local received_files = {}; + for _, child in ipairs(files.tags) do + if child.name == 'file' then + received_files[child.attr.id] = json.decode(child:get_text()); + end + end + + -- fire events so file sharing component will add/remove files and will notify clients + local removed, added = table_compare(room.jitsi_shared_files or {}, received_files) + for _, id in ipairs(removed) do + module:context(local_domain):fire_event('jitsi-filesharing-remove', { + room = room; id = id; + }); + end + for _, id in ipairs(added) do + module:context(local_domain):fire_event('jitsi-filesharing-add', { + room = room; file = received_files[id]; + }); + end + end + if fire_jicofo_unlock then -- everything is connected allow participants to join module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; }); diff --git a/resources/prosody-plugins/mod_visitors.lua b/resources/prosody-plugins/mod_visitors.lua index b0719e0792..687c0e1c91 100644 --- a/resources/prosody-plugins/mod_visitors.lua +++ b/resources/prosody-plugins/mod_visitors.lua @@ -16,6 +16,7 @@ local is_admin = util.is_admin; local presence_check_status = util.presence_check_status; local process_host_module = util.process_host_module; local is_transcriber_jigasi = util.is_transcriber_jigasi; +local json = require 'cjson.safe'; local MUC_NS = 'http://jabber.org/protocol/muc'; @@ -78,6 +79,17 @@ local function send_visitors_iq(conference_service, room, type) end visitors_iq:up(); + + -- files that are shared in the room + if room.jitsi_shared_files then + visitors_iq:tag('files', { xmlns = 'jitsi:visitors' }); + for k, v in pairs(room.jitsi_shared_files) do + visitors_iq:tag('file', { + id = k + }):text(json.encode(v)):up(); + end + visitors_iq:up(); + end end visitors_iq:up(); @@ -370,23 +382,17 @@ process_host_module(main_muc_component_config, function(host_module, host) end, -2); end); -module:hook('jitsi-lobby-enabled', function(event) +local function update_vnodes_for_room(event) local room = event.room; - if visitors_nodes[room.jid] then - -- we need to update all vnodes - local vnodes = visitors_nodes[room.jid].nodes; - for conference_service in pairs(vnodes) do - send_visitors_iq(conference_service, room, 'update'); + if visitors_nodes[room.jid] then + -- we need to update all vnodes + local vnodes = visitors_nodes[room.jid].nodes; + for conference_service in pairs(vnodes) do + send_visitors_iq(conference_service, room, 'update'); + end end - end -end); -module:hook('jitsi-lobby-disabled', function(event) -local room = event.room; - if visitors_nodes[room.jid] then - -- we need to update all vnodes - local vnodes = visitors_nodes[room.jid].nodes; - for conference_service in pairs(vnodes) do - send_visitors_iq(conference_service, room, 'update'); - end - end -end); +end + +module:hook('jitsi-lobby-enabled', update_vnodes_for_room); +module:hook('jitsi-lobby-disabled', update_vnodes_for_room); +module:hook('jitsi-filesharing-updated', update_vnodes_for_room); diff --git a/resources/prosody-plugins/util.lib.lua b/resources/prosody-plugins/util.lib.lua index 860689a068..ac185546cf 100644 --- a/resources/prosody-plugins/util.lib.lua +++ b/resources/prosody-plugins/util.lib.lua @@ -616,6 +616,28 @@ local function table_add(t1, t2) end end +-- Returns as a first result the removed items and as a second the added items +local function table_compare(old_table, new_table) + local removed = {} + local added = {} + + -- Find removed items (in old but not in new) + for id, _ in pairs(old_table) do + if new_table[id] == nil then + table.insert(removed, id) + end + end + + -- Find added items (in new but not in old) + for id, _ in pairs(new_table) do + if old_table[id] == nil then + table.insert(added, id) + end + end + + return removed, added +end + -- Splits a string using delimiter function split_string(str, delimiter) str = str .. delimiter; @@ -697,6 +719,7 @@ return { starts_with = starts_with; starts_with_one_of = starts_with_one_of; table_add = table_add; + table_compare = table_compare; table_shallow_copy = table_shallow_copy; table_find = table_find; };