Files
vnctalk-prosody/vnctalk/mod_vnc_fcm.lua
T

983 lines
39 KiB
Lua

--require "socket"
--rawset(_G, "PROXY", false); -- socket module accesses global variable PROXY
local http = require "net.http"
local json = require "util.json"
-- local ltn12 = require "ltn12"
local jid_bare = require "util.jid".bare;
local jid_split = require "util.jid".split;
local timer = require "util.timer";
local st = require "util.stanza";
local time_now = os.time;
local private_storage = module:open_store("private");
local vcard_storage = module:open_store("vcard");
local config_store = nil;
local fcm_token_store = module:open_store("fcmtoken");
local fcm_token_map_store = module:open_store("fcmtoken", "map");
local fcmAPIKey = module:get_option_string("fcm_api_key");
local fcmAPIURL = module:get_option_string("fcm_api_url");
local moduleIsActive = fcmAPIURL and fcmAPIKey;
local isMUC = module:get_host_type() == "component";
local ThisDomain = module.host;
local xmlns_sm2 = "urn:xmpp:sm:2";
local xmlns_sm3 = "urn:xmpp:sm:3";
local ThisBroadcast = "broadcast@"..ThisDomain;
module:add_feature("urn:xmpp:vnctalk:fcm");
local inactive_devices = {};
local UDomain = ThisDomain;
if isMUC then
local storage_host = module:get_option_string("storage_host");
UDomain = storage_host;
module:log("debug", "opening stores for host %s", storage_host);
timer.add_task(3, function ()
private_storage = module:open_store(storage_host, "private");
vcard_storage = module:open_store(storage_host, "vcard");
fcm_token_store = module:open_store(storage_host, "fcmtoken");
fcm_token_map_store = module:open_store(storage_host, "fcmtoken", "map");
if not private_storage or not vcard_storage then
module:log("debug", "private/vcard_store %s/%s not found? - will try again", private_storage, vcard_storage);
return 3;
else
module:log("debug", "private/vcard_store are now %s/%s", private_storage, vcard_storage);
end
end
);
config_store = module:open_store("config");
end
local function contains(list, x)
if (list) then
for _, v in pairs(list) do
if (v == x) then return true end
end
end
return false
end
local function starts_with(str, start)
return str:sub(1, #start) == start
end
local function dumpTable(t, depth)
local result = "";
if (not depth) then depth = 0 end;
local prefix = string.rep(" ", depth);
if (type(t) ~= "table") then
result = result.." "..tostring(t);
else
for key,value in pairs(t) do
result = result..prefix..tostring(key).." => "..dumpTable(value, depth+1).."\n"
end
end
return result;
end
local function deserializeDataElement(pdElement, expectedName)
--module:log("debug", "deserializeDataElement("..expectedName..", "..dumpTable(pdElement)..")");
if (not pdElement) then return nil end
if (type(pdElement) ~= "table") then return nil end
if (pdElement[1] and pdElement["attr"] and pdElement["attr"]["key"] and (pdElement["attr"]["key"] == expectedName))
then
local result = json.decode(pdElement[1]);
--module:log("debug", "deserializeDataElement() = '"..dumpTable(result).."'");
return result;
end
return nil;
end
local function trim(s)
if s and (type(s) == "string") then
return (s:gsub("^%s*(.-)%s*$", "%1"))
else
return s
end
end
local function collapseSpaces(s)
if s and (type(s) == "string") then
return (s:gsub("%s+", " "))
else
return s
end
end
local function prettyUsername(userName)
userName = string.upper(string.sub(userName, 1, 1))..string.sub(userName, 2, -1) -- upcase first char in any case
if string.find(userName, "%.") -- contains dot
then
local i = 1
while true do
local j = string.find(userName, "%.", i)
if j == nil or #userName == i then break end
-- replace dot with space and upcase next char
userName = string.sub(userName, 1, j-1).." "..string.upper(string.sub(userName, j+1, j+1))..string.sub(userName, j+2, -1)
if #userName <= j then break end
i = j
end
end
return trim(collapseSpaces(userName));
end
local function getDisplayName(userName)
local vCard, err = vcard_storage:get(userName);
if vCard then
vCard = st.deserialize(vCard);
end
local name = nil;
if not vCard or err then
module:log("error", "Unable to get vCard for %s : %s ", userName, tostring(err));
else
name = vCard:find("{vcard-temp}FN#");
end
if not name then name = prettyUsername(userName) or userName end
module:log("debug", "name for %s : %s", userName, name)
return name;
end
local function removeKeyFromUser(userName, fcmId)
local pd, pfFetchError = private_storage:get(userName);
if (pfFetchError) then
module:log(
"error",
"Fetching private data for '%s' failed : %s", tostring(userName), tostring(pfFetchError)
);
return 0,{};
elseif (pd) then
local removed = false;
for key,value in pairs(pd)
do
--module:log("debug", "PDD("..userName..") : "..tostring(key).." => "..tostring(value));
if ((key == "documents:stanza:io:json") and (type(value) == "table")) then
--module:log("debug", "PDD.stanza("..userName..") : "..json.encode(value));
for k2,v2 in pairs(value) do
local fcm = deserializeDataElement(v2, "fcm");
if (fcm)
then
module:log("debug", "removing fcmID %s from private data record: %s", fcmId, json.encode(fcm));
fcm[fcmId] = nil;
v2[1]=json.encode(fcm);
removed = true;
--module:log("debug", "AFTER PDD.stanza: "..json.encode(v2));
--module:log("debug", "AFTER PDD: "..json.encode(value));
--module:log("debug", "removed outdated key");
break;
end
end
end
end
if (removed)
then
local ok, errmsg = private_storage:set(userName, pd);
if (not ok)
then
module:log("error", "failed to store private data for %s : '%s'", userName, tostring(errmsg));
end
else
module:log("debug", "unable to remove fcmID %s from %s. Not found in data: %s", fcmId, userName, tostring(pd));
end
end
end
local function getPostCallback(_fcmId, _userName)
return function(body, code, response)
local shortKey = string.sub(_fcmId, 1, 6)
if code ~= 200 then
module:log("error", "FCM result %s@%s : HTTP %s %s", _userName, shortKey, tostring(code), tostring(body))
-- if body then module:log("error", body); end
else
module:log("debug", "FCM result %s@%s : HTTP %s %s", _userName, shortKey, tostring(code), tostring(body))
local response2 = json.decode(body)
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "NotRegistered"
then
-- module:log("debug", "%s key %s is outdated", _userName, shortKey);
removeKeyFromUser(_userName, _fcmId);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "MissingRegistration"
then
-- module:log("debug", "%s key %s is never registered", _userName, shortKey);
removeKeyFromUser(_userName, _fcmId);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "InvalidRegistration"
then
-- module:log("debug", "%s key %s is invalid", _userName, shortKey);
removeKeyFromUser(_userName, _fcmId);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "InvalidPackageName"
then
-- module:log("debug", "%s key %s is using invalid FCM package name", _userName, shortKey);
removeKeyFromUser(_userName, _fcmId);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "error:MessageTooBig"
then
-- module:log("debug", "%s key %s : message too big", _userName, shortKey);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "InvalidDataKey"
then
-- module:log("debug", "%s key %s is using invalid data key", _userName, shortKey);
end
if response2.results and (type(response2.results) == "table") and response2.results[1] and response2.results[1]["error"] == "InvalidTtl"
then
--- module:log("debug", "%s key %s is using invalid TTL", _userName, shortKey);
end
end
return false -- close request
--return true -- keep request open
end
end
local function postFCM(_userName, _fcmId, _source, _body, _type, _senderName, _groupTopic, _attachment_type, _lang, _msgid, _vncTalkConferenceEtype, _vncTalkConferenceId, _nFromJid, _jitsiURL, _jitsiRoom, _isMessageCorrection, _enableSoundOpt)
if (not moduleIsActive) then return end;
local domainname = "";
local v_attachment = "";
if isMUC then
domainname = module:get_option_string("storage_host");
else
domainname = module.host;
end
if (_attachment_type ~= "") and (_attachment_type ~=nil) then
v_attachment = _attachment_type;
end
local lsource = _source;
local ltype = _type;
local signal = nil;
local nfrom = _source;
local nJitsiURL = "";
local nJitsiRoom = "";
local replaceMsgId = "";
if (_isMessageCorrection ~= nil) then
replaceMsgId = _isMessageCorrection;
end
if (_vncTalkConferenceId ~= nil) then
if string.match(_vncTalkConferenceId, "@") then
lsource = _vncTalkConferenceId;
end
end
if (_vncTalkConferenceEtype ~=nil) then
signal = "1";
ltype = _vncTalkConferenceEtype;
end
if (_nFromJid ~= nil) then
nfrom = _nFromJid;
end
if (_jitsiURL ~= nil) then
nJitsiURL = _jitsiURL;
end
if (_jitsiRoom ~= nil) then
nJitsiRoom = _jitsiRoom;
end
local stamp = os.time();
local fcmRequest = {
to = _fcmId,
enableSoundOpt = _enableSoundOpt,
content_available = true,
priority = "high",
data = {
nType = "local_notification", -- notification type
eType = ltype, -- event type
jid = lsource, -- source jid (room jid or 1:1 chat peer bare jid)
conferenceId = _vncTalkConferenceId,
nfrom = nfrom,
nto = _userName.."@"..domainname,
name = _senderName, -- group chat nick name or single chat peer vCard name",
gt = _groupTopic, -- topic of room iff groupchat
aft = v_attachment,
callSignal = signal,
jitsiURL = nJitsiURL,
jitsiRoom = nJitsiRoom,
t = stamp,
lang = _lang,
msgid = _msgid,
replaceid = replaceMsgId,
body = _body -- notification body/text content
}
};
fcmRequest["android"] = { priority = "high", ttl = 345600 };
fcmRequest["webpush"] = { headers = { Urgency = "high", TTL = 345600 } };
local data = json.encode(fcmRequest);
local httpRequestOptions = {
method = "POST",
body = data,
headers = {
["Connection"] = 'close',
["Authorization"] = "key="..fcmAPIKey,
["Content-Type"] = "application/json; charset=utf-8"
}
}
module:log("info", "sending notification to %s for token %s - from: %s - msgid: %s - body: %s", _userName.."@"..domainname, _fcmId, _source, _msgid, _body);
-- ssl does not seem to work with net.http, so we proxy it
--http.request("https://fcm.googleapis.com/fcm/send", httpRequestOptions, postCallback)
module:log("info", "sendingHTTP request to %s", fcmAPIURL);
http.request(fcmAPIURL, httpRequestOptions, getPostCallback(_fcmId, _userName))
end
local function postFCMIOS(_userName, _fcmId, _source, _body, _type, _senderName, _groupTopic, _attachment_type, _lang, _msgid, _vncTalkConferenceEtype, _vncTalkConferenceId, _nFromJid, _jitsiURL, _jitsiRoom, _isMessageCorrection, _enableSoundOpt)
if (not moduleIsActive) then return end;
local domainname = "";
local ntitle = "";
local nbody = "";
local v_attachment = "";
local nJitsiURL = "";
local nJitsiRoom = "";
local replaceMsgId = "";
if (_isMessageCorrection ~= nil) then
replaceMsgId = _isMessageCorrection;
end
if isMUC then
domainname = module:get_option_string("storage_host");
if (_groupTopic ~= "") and (_groupTopic ~= nil) then
ntitle = _groupTopic;
else
ntitle = jid_split(_source);
end
else
domainname = module.host;
if (_groupTopic ~= "") and (_groupTopic ~=nil) then
ntitle = _groupTopic
else
if (_senderName ~= "") and (_senderName ~= nil) then
ntitle = _senderName;
else
ntitle = _source;
end
end
end
if (_attachment_type ~= "") and (_attachment_type ~=nil) then
v_attachment = _attachment_type;
end
local _click_action = _type:upper();
if (_click_action == "NORMAL") then
_click_action = "CHAT";
end
local lsource = _source;
local ltype = _type;
local signal = nil;
local nfrom = _source;
if (_vncTalkConferenceId ~= nil) then
if string.match(_vncTalkConferenceId, "@") then
lsource = _vncTalkConferenceId;
end
end
if (_vncTalkConferenceEtype ~=nil) then
signal = "1";
ltype = _vncTalkConferenceEtype;
end
if (_nFromJid ~= nil) then
nfrom = _nFromJid;
end
if (_jitsiURL ~= nil) then
nJitsiURL = _jitsiURL;
end
if (_jitsiRoom ~= nil) then
nJitsiRoom = _jitsiRoom;
end
module:log("info", "sending IOS sound %s", _enableSoundOpt);
local fcmRequest = {
to = _fcmId,
priority = "high",
enableSoundOpt = _enableSoundOpt,
notification = {
title = ntitle,
body = _body,
mutable_content = true,
sound = "incoming-call-loop.caff",
thread_id = lsource,
click_action = _click_action
},
data = {
nType = "local_notification", -- notification type
eType = ltype, -- event type
jid = lsource, -- source jid (room jid or 1:1 chat peer bare jid)
conferenceId = _vncTalkConferenceId,
nfrom = nfrom,
nto = _userName.."@"..domainname,
name = _senderName, -- group chat nick name or single chat peer vCard name",
gt = ntitle, -- topic of room iff groupchat
aft = v_attachment,
callSignal = signal,
jitsiURL = nJitsiURL,
jitsiRoom = nJitsiRoom,
lang = _lang,
msgid = _msgid,
replaceid = replaceMsgId,
body = _body -- notification body/text content
}
};
local data = json.encode(fcmRequest);
local httpRequestOptions = {
method = "POST",
body = data,
headers = {
["Connection"] = 'close',
["Authorization"] = "key="..fcmAPIKey,
["Content-Type"] = "application/json; charset=utf-8"
}
}
-- module:log("debug", "sending notification to %s for token %s - from: %s - msgid: %s - body: %s", _userName.."@"..domainname, _fcmId, _source, _msgid, string.sub(_body, 20).."...");
module:log("debug", "sending notification to %s for token %s - from: %s - msgid: %s - body: %s", _userName.."@"..domainname, _fcmId, _source, _msgid, _body.."...");
-- ssl does not seem to work with net.http, so we proxy it
--http.request("https://fcm.googleapis.com/fcm/send", httpRequestOptions, postCallback)
http.request(fcmAPIURL, httpRequestOptions, getPostCallback(_fcmId, _userName))
end
local function getNotifyOptionsForUser(userName)
-- module:log("info", "in getNotifyOptionsForUser %s", userName );
local pd, pfFetchError = private_storage:get(userName);
if (pfFetchError) then
module:log(
"error",
"Fetching private data for '%s' failed : %s", tostring(userName), tostring(pfFetchError)
);
return 0,{};
else
local tokens;
local empty;
local nType;
local nLang = "en";
local globalMute = false;
local enableSound;
if (pd) then
for key,value in pairs(pd)
do
-- module:log("debug", "PD("..userName..") : "..tostring(key).." => "..tostring(value));
module:log("debug", "PD( %s) : %s => %s ", userName,tostring(key),tostring(value));
if ((key == "documents:stanza:io:json") and (type(value) == "table")) then
--module:log("debug", "PD("..userName..") : "..json.encode(value));
-- module:log("debug", "PD( %s ) : %s ", userName ,json.encode(value));
for k2,v2 in pairs(value) do
if (not tokens) then
local fcm = deserializeDataElement(v2, "fcm");
if (type(fcm) == "table") then
tokens = {};
for token,x in pairs(fcm) do
-- module:log("debug", "token: %s - x[os]: %s", token, x["os"]);
if (x["os"] ~= nil) then
table.insert(tokens, token.."@"..x["os"])
end
end
end
end
if (not nType) then
local options = deserializeDataElement(v2, "notifyOptions");
if (options and (type(options) == "table") and options["type"]) then
nType = tonumber(options["type"]);
module:log("info", "got nType: %s", nType);
end
end
if (not nType) then
local options = deserializeDataElement(v2, "notification");
module:log("info", "got new NTYPE option %s", options);
if (options) then
nType = tonumber(options);
module:log("info", "got nType: %s", nType);
end
end
if (enableSound == nil) then
local enableSoundOpt = deserializeDataElement(v2, "enabledSound");
module:log("info", "got new SOUND option %s", enableSoundOpt);
enableSound = enableSoundOpt;
end;
local pGlobalMute = deserializeDataElement(v2, "globalMute");
if (pGlobalMute) then
module:log("info", "got pGlobalMute: %s", pGlobalMute);
nType = 0;
tokens = {};
end
if (not nLang) then
local options = deserializeDataElement(v2, "lang");
-- module:log("debug", "PDLang: %s => %s", userName, json.encode(options));
if (options) and (type(options) == "string") then
nLang = options;
end;
-- module:log("debug", "type of options: %s", type(options));
end
end
break; -- we can stop after the documents:stanza:io:json element
end
end
--module:log("debug", "getNotifyOptionsForUser("..userName..") = "..tostring(tokens).." => "..tostring(nType));
end
local map_res = fcm_token_store:get(userName);
-- module:log("debug", "result from map store for %s is: %s", userName, dumpTable(map_res));
if (map_res) then
for _device, _tkr in pairs(map_res) do
-- module:log("debug", "got for %s device %s => %s", userName, _device, dumpTable(_tkr));
-- module:log("debug", "got for %s device %s => token: %s - os: %s", userName, _device, _tkr.token, _tkr.os);
local sessions = prosody.bare_sessions;
local ubjid = userName.."@"..UDomain;
local u_sessions = sessions[ubjid] or nil;
local deviceonline = false;
if (u_sessions) then
local u_sessions_device = u_sessions["sessions"] or nil;
if (u_sessions_device) then
module:log("debug", "devise is %s", _device);
-- module:log("debug", "u_sessions_device %s", dumpTable(u_sessions_device));
local dsession = u_sessions_device[_device] or nil;
-- module:log("debug", "dsession %s", dumpTable(dsession));
if (dsession) then
local hibernated = dsession["hibernated"] or nil;
local hibernating = dsession["hibernating"] or nil;
local awaiting_ack = dsession["awaiting_ack"] or nil;
module:log("info", "found session for _device %s - hibernating: %s, hibernated %s", _device, hibernating, hibernated);
if not ((hibernated) or (hibernating)) then
local dev_in_background = inactive_devices[_device] or nil;
if dev_in_background then
if awaiting_ack then
module:log("info", "device %s is still connected, but inactive and awaiting_ack", _device);
else
module:log("info", "device %s is still connected, but inactive", _device);
deviceonline = true;
end
else
if awaiting_ack then
module:log("info", "device %s is connected and active, but awaiting_ack", _device);
else
module:log("info", "device %s is connected and active", _device);
deviceonline = true;
end
end
end
end
end
end
if not(deviceonline) then
local already_added = contains(tokens, _tkr.token.."@".._tkr.os);
module:log("debug","already_added: %s", _tkr.token.."@".._tkr.os);
if not(tokens) then
tokens = {};
end
if not(already_added) then
table.insert(tokens, _tkr.token.."@".._tkr.os);
end
end
-- module:log("debug","tokens table is now: %s", dumpTable(tokens));
end
end
if (not tokens) then tokens = {}; end;
if (not nType) then nType = 2; end;
--module:log("debug", "getNotifyOptionsForUser(%s) = %i, %i tokens", userName, nType, table.getn(tokens));
-- module:log("debug", "getNotifyOptionsForUser(%s) = %i, %i tokens, lang %s", userName, nType, table.getn(tokens), nLang);
local sessions = prosody.bare_sessions;
local ubjid = userName.."@"..ThisDomain;
for _user, _resdetails in pairs(sessions) do
if (_user == ubjid) then
for _res, _detail1 in pairs(_resdetails["sessions"]) do
local hibernated = _detail1["hibernated"] or _detail1["hibernating"] or nil;
if (hibernated) then
module:log("info", "for _user: %s and _res: %s got hibernated since: %s", _user, _res, hibernated);
else
module:log("info", "for _user: %s and _res: %s got active session?", _user, _res);
--module:log("debug", "for _user: %s and _res: %s got detail: %s", _user, _res, dumpTable(_detail1));
end
end
-- module:log("debug", "session for _res %s is: %s", _res, dumpTable(_resdetails));
end
end
-- module:log("debug", "sessions: %s", dumpTable(sessions[ubjid]));
return nType, tokens, nLang, enableSound;
end
end
local function fcm_notify(userName, title, body, type, _senderName, _groupTopic, vnc_attachment_type, msgid, vncTalkConferenceEtype, vncTalkConferenceId, _nFrom, _jitsiURL, _jitsiRoom, _isMessageCorrection)
if (not moduleIsActive) then return end;
local nType, tokens, lang, soundOpt = getNotifyOptionsForUser(userName);
if (tokens and (#tokens) > 0) and ((nType == 1) or (nType == 2) or vncTalkConferenceEtype)
then
module:log(
"info", "in fcm_notify(%s, %s, %s) => type=%s, %s tokens",
userName, title, body, tostring(nType), tostring((#tokens))
);
local sName = _senderName;
local gTopic = _groupTopic;
if (nType == 1) then
sName = "new message";
gTopic = "new message";
body = "";
end
if (_isMessageCorrection ~= nil) then
body = " ";
end;
for i,token in pairs(tokens) do
if (token)
then
local rtoken, ros = jid_split(token);
-- module:log("debug","got rtoken: %s - os: %s ", rtoken, ros);
if (ros == "ios") then
-- module:log("debug", "postFCMIOS with (%s, %s, %s, %s, %s, %s, %s, %s, %s)", userName, rtoken, title, body, type, _senderName, _groupTopic, vnc_attachment_type, lang);
postFCMIOS(userName, rtoken, title, body, type, sName, gTopic, vnc_attachment_type, lang, msgid, vncTalkConferenceEtype, vncTalkConferenceId, _nFrom, _jitsiURL, _jitsiRoom, _isMessageCorrection, soundOpt);
else
-- module:log("debug", "postFCM with (%s, %s, %s, %s, %s, %s, %s, %s, %s)", userName, rtoken, title, body, type, _senderName, _groupTopic, vnc_attachment_type, lang, _nFrom);
postFCM(userName, rtoken, title, body, type, sName, gTopic, vnc_attachment_type, lang, msgid, vncTalkConferenceEtype, vncTalkConferenceId, _nFrom, _jitsiURL, _jitsiRoom, _isMessageCorrection, soundOpt);
end
end
end
else
module:log("debug", "fcm_notify(%s, %s, %s) => type=%s, no tokens", userName, title, body, tostring(nType));
end
end
-- build map from room nicknames to real JIDs
local room_nick_jid = {} -- key1=roomJid, key2=occupantNickname, value=occupantJid
local room_afffiliations = {} -- key1=roomJid, key2=occupantNickname, value=occupantJid
module:hook("muc-room-changed", function (event)
--module:log("debug", "muc-room-changed "..event.room.jid.."\n"..tostring(event.room));
--if not room_nick_jid[event.room.jid]
--then
-- module:log("debug", "muc: new room "..tostring(event.room))
--else
-- module:log("debug", "muc: "..tostring(event.room).." changed")
--end
room_nick_jid[event.room.jid] = {} -- reset mapping table for the current room
local nick_jid = room_nick_jid[event.room.jid] -- mapping table for the current room
for nick, odata in pairs(event.room._occupants)
do
local jid = jid_bare(odata["jid"])
nick_jid[nick]=jid
-- module:log("debug", "muc nick "..nick.." => "..jid.." in "..event.room.jid);
end
-- copy affiliations table
room_afffiliations[event.room.jid] = {}
for bareJid, role in pairs(event.room._affiliations)
do
room_afffiliations[event.room.jid][bareJid] = role;
-- module:log("debug", "muc role %s %s %s", event.room.jid, bareJid, role);
end
end);
local function message_handler(event, fromLocal)
--TODO; use stanza:find here
-- module:log("debug", "MH0");
local origin, stanza = event.origin, event.stanza;
local message_id = event.stanza.attr.id;
-- module:log("debug","message id: %s", event.stanza.attr.id);
local type = stanza.attr.type or "normal";
local from = jid_bare(stanza.attr.from);
local to = jid_bare(stanza.attr.to) or from;
local vncTalkConferenceEtype = stanza:find("{xmpp:vnctalk}vncTalkConference/eventType#") or nil;
local vnctalk_broadcast = stanza:find("{xmpp:vnctalk}vncTalkBroadcast", "xmpp:vnctalk" ) or false;
-- module:log("info", "vnctalk_avatarup broadcast: %s", vnctalk_broadcast);
local vnctalk_avatarup = false;
if (vnctalk_broadcast) then
vnctalk_avatarup = vnctalk_broadcast.attr.avatarup or false;
end
if (to == ThisBroadcast) then
vnctalk_avatarup = true;
end
module:log("info", "handling message from=%s, to=%s, fromLocal=%s", from, to, fromLocal);
if ((to ~= from) or ((to == from) and vncTalkConferenceEtype))
then
local content = trim(stanza:find("body#"));
local delayedDelivery = stanza:get_child("delay", "urn:xmpp:delay");
-- if we have a vncTalk incoming or whiteboard element, it overrides the type
local vncTalkIncomingType = stanza:find("{xmpp:vnctalk}vncTalkConference/conferenceType#");
local vncTalkConferenceId = stanza:find("{xmpp:vnctalk}vncTalkConference/conferenceId#") or nil;
local jitsiURL = stanza:find("{xmpp:vnctalk}vncTalkConference/jitsiURL#") or nil;
local jitsiRoom = stanza:find("{xmpp:vnctalk}vncTalkConference/jitsiRoom#") or nil;
-- module:log("debug", "vncTalkIncomingType: %s", vncTalkIncomingType);
local vncTalkWhiteboard = stanza:find("{xmpp:vnctalk}whiteboard");
local isSentCarbonMessage = stanza:get_child("sent", "urn:xmpp:carbons:2");
local vnc_attachment_type = stanza:find("{xmpp:vnctalk}attachment/fileType#");
local vnctalk_broadcast = stanza:get_child("vncTalkBroadcast", "xmpp:vnctalk");
local vnctalk_broadcast_childid = nil;
if vnctalk_broadcast then
if starts_with(to, "broadcast") then
-- module:log("message is to broadcast - bailing out");
return nil;
end
vnctalk_broadcast_childid = vnctalk_broadcast.attr.id;
-- module:log("debug", "this is a broadcast message: %s", tostring(stanza));
local vnctalk_broadcast_title = vnctalk_broadcast.attr.title;
-- module:log("debug", "this is a broadcast title: %s", vnctalk_broadcast_title);
local vnctalk_broadcast_target = vnctalk_broadcast.attr.origtarget;
-- module:log("debug", "this is a broadcast target: %s", vnctalk_broadcast_target);
local body = stanza:get_child("body");
-- module:log("debug", "broadcast body: %s", content);
-- module:log("debug", "jid_split(to): %s", jid_split(to));
if (not(vnctalk_avatarup)) then
fcm_notify(jid_split(to), vnctalk_broadcast_target, content, type, getDisplayName(jid_split(from)), "Broadcast: "..vnctalk_broadcast_title, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, nil, jitsiURL, jitsiRoom);
end
return nil;
end
local isMessageCorrection = stanza:find("{urn:xmpp:message-correct:0}replace@id") or nil;
if (isMessageCorrection) then
content = " ";
type = "CORRECTION";
end
-- debug from here
module:log("debug","continue to process message with body: %s", content);
module:log("info","isMessageCorrection: %s", dumpTable(isMessageCorrection));
local auxType = vncTalkIncomingType or (vncTalkWhiteboard and "whiteboard");
if (auxType)
then
content = auxType;
type = auxType;
end;
local doNotify = content and (content ~= " ") and (content ~= "") and (not(vnctalk_avatarup)) and (delayedDelivery == nil) and (isSentCarbonMessage == nil) and (
(type == "chat") or (type == "groupchat") or (type == "audio") or (type == "video") or (type == "whiteboard") or (type == "screen") or (type == "CORRECTION")
);
if (doNotify)
then
module:log("debug", "536-doNotify: %s -> %s fl=%s, type=%s, content=%s, carbon=%s, isMUC=%s => doNotify=%s",
stanza.attr.from, stanza.attr.to, tostring(fromLocal), type, content, isSentCarbonMessage, tostring(isMUC), tostring(doNotify));
if (not isMUC)
then
module:log("info", "537-doNotify not isMUC: %s -> %s fl=%s, type=%s, content=%s, carbon=%s, isMUC=%s => doNotify=%s",
stanza.attr.from, stanza.attr.to, tostring(fromLocal), type, content, isSentCarbonMessage, tostring(isMUC), tostring(doNotify));
--if (fromLocal)
--then
-- module:log("debug", " ignoring message from local client");
--module:log("debug", "not sending fromLocal from=%s, to=%s, type=%s, content=%s, isSentCarbonMessage=%s", from, to, type, content, isSentCarbonMessage);
local fromuser2, fromdomain2 = jid_split(stanza.attr.from);
local touser2, todomain2 = jid_split(stanza.attr.to);
module:log("info", "fromdomain2: %s <=> ThisDomain: %s", fromdomain2, ThisDomain);
module:log("info", "todomain2: %s <=> ThisDomain: %s", todomain2, ThisDomain);
if (fromLocal and (fromdomain2 == ThisDomain) and ((todomain2 == ThisDomain) or (todomain2 == nil))) and (type ~= "groupchat") then
module:log("info", "checkssa1");
fcm_notify(jid_split(to), from, content, type, getDisplayName(jid_split(from)), nil, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, nil, jitsiURL, jitsiRoom, isMessageCorrection);
else
module:log("info", "checkssa2");
if (type ~= "groupchat") and (fromdomain2 ~= ThisDomain) then
fcm_notify(jid_split(to), from, content, type, getDisplayName(jid_split(from)), nil, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, nil, jitsiURL, jitsiRoom, isMessageCorrection);
else
-- notify when not from a local domain
if not (prosody.hosts[fromdomain2]) then
module:log("info", "I do not know about %s", fromdomain2);
end
if not (prosody.hosts[fromdomain2]) and type == "groupchat" then
local rjnode, rjhost, rjres = jid_split(stanza.attr.from);
local rdn, rdh = jid_split(rjres);
local tdn, tdh, tdr = jid_split(stanza.attr.to);
if ((rdn == tdn) and (rdh == tdh)) then
module:log("info", "766 ... rdn=%s, tdn=%s, rdh=%s, tdh=%s", rdn, tdn, rdh, tdh);
else
module:log("info", "541-doNotify IOM: %s -> %s fl=%s, type=%s, content=%s, carbon=%s, isMUC=%s => doNotify=%s, rdn=%s",
stanza.attr.from, stanza.attr.to, tostring(fromLocal), type, content, isSentCarbonMessage, tostring(isMUC), tostring(doNotify), rdn);
fcm_notify(jid_split(to), from, content, type, getDisplayName(rdn), nil, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, nil, jitsiURL, jitsiRoom, isMessageCorrection);
end
else
module:log("debug", " WTF ignoring message");
end
end
end
--end
else -- MUC
module:log("debug", "538-doNotify isMUC: %s -> %s fl=%s, type=%s, content=%s, carbon=%s, isMUC=%s => doNotify=%s",
stanza.attr.from, stanza.attr.to, tostring(fromLocal), type, content, isSentCarbonMessage, tostring(isMUC), tostring(doNotify));
local muc, host = jid_split(to);
if prosody.hosts[host] then
module:log("info","muc conf: %s", dumpTable(prosody.hosts[host].modules.muc));
-- module:log("debug","muc conf: %s", dumpTable(prosody.hosts[host].modules.muc.rooms[to]));
-- module:log("debug","muc affiliations: %s", dumpTable(prosody.hosts[host].modules.muc.rooms[to]._affiliations));
module.log("info", "processing to and found local muc component: %s", to);
local affs = prosody.hosts[host].modules.muc.rooms[to]._affiliations;
if affs then
for _aff, role in pairs(affs) do
local now = time_now();
local roomTopic = prosody.hosts[host].modules.muc.rooms[to]._data.subject or nil;
module:log("info", "got muc _aff %s from muc %s", _aff, to);
if from ~= _aff then
module:log("info", " notify MUC affilaite Jid=%s", _aff);
module:log("debug", "muc notifiy from: %s => to: %s", from, to);
local roomJid = to;
fcm_notify(jid_split(_aff), roomJid, content, type, getDisplayName(jid_split(from)), roomTopic, vnc_attachment_type, message_id, vncTalkConferenceEtype, vncTalkConferenceId, from, jitsiURL, jitsiRoom, isMessageCorrection);
end
end
end
end
end
else
module:log("debug", "ignoring from=%s, to=%s, type=%s, content=%s", from, to, type, content);
end
end
return;
end
local function iom_muc_handler(event)
-- module:log("debug", "iom muc handler - event: %s", dumpTable(event));
local origin, stanza = event.origin, event.stanza;
local message_id = event.stanza.attr.id;
-- module:log("debug","message id: %s", event.stanza.attr.id);
local type = stanza.attr.type or "normal";
local from = jid_bare(stanza.attr.from);
local to = jid_bare(stanza.attr.to) or from;
local fromloc,fromhost = jid_split(from);
local toloc,tohost = jid_split(to);
if (prosody.hosts[tohost] and (prosody.hosts[fromhost] == nil)) then
module:log("debug", "iom message type %s from host %s - to host %s", type, fromhost, tohost);
return message_handler(event, false);
else
return nil;
end
end
local function message_handler_to_local(event)
return message_handler(event, false);
end
local function message_handler_from_local(event)
return message_handler(event, true);
end
local function logCsiEvent(event, active)
local csires = event.origin.resource or nil;
if active
then
inactive_devices[csires] = nil;
module:log("debug", "ACTIVE: %s", tostring(event.origin));
module:log("debug", "ACTIVE resource %s", csires);
-- module:log("debug", "ACTIVE: %s", dumpTable(event.origin));
else
module:log("debug", "INACTIVE: %s", tostring(event.origin));
-- module:log("debug", "INACTIVE: %s", dumpTable(event.origin));
module:log("debug", "INACTIVE resource %s", csires);
local t = os.time();
inactive_devices[csires] = t;
end
end
local function handle_iq(event)
local origin, stanza = event.origin, event.stanza;
local from_node, from_host, from_res = jid_split(stanza.attr.from);
local reply = st.reply(stanza);
local res = false;
if stanza.attr.type == "set" then
local fcm_child = stanza:get_child("add", "xmpp:vnctalk:fcm") or nil;
if (fcm_child) then
local fcm_token = fcm_child:get_child("fcm", "xmpp:vnctalk:fcm") or nil;
if (fcm_token) then
local _device = fcm_token.attr.device or nil;
local _token = fcm_token.attr.token or nil;
local _os = fcm_token.attr.os or nil;
module:log("debug", "going to add token for %s - device %s, token: %s ", from_node, _device, _token);
local do_add_token = (_device) and (_token) and (_os);
if (do_add_token) then
local tk_os = { token = _token , os = _os };
local _store_fcm = fcm_token_map_store:set(from_node, _device, tk_os);
res = true;
origin.send(st.reply(stanza):tag('add', {xmlns='xmpp:vnctalk:fcm'}):text("ok"));
return true;
-- module:log("debug", "going to add token for ")
end
end
end
end
if res then
return true;
end
end
if (moduleIsActive)
then
-- Stanszas to local clients
module:hook("message/bare", message_handler_to_local, 2);
module:hook("message/full", iom_muc_handler, 2);
--module:hook("message/full", message_handler_to_local, 2);
-- Stanzas sent by local clients
module:hook("pre-message/bare", message_handler_from_local, 2);
module:hook("pre-message/full", message_handler_from_local, 2);
module:hook("vnc-rest-message", message_handler_from_local, 2);
module:hook("csi-client-active", function(event) logCsiEvent(event, true); end);
module:hook("csi-client-inactive", function(event) logCsiEvent(event, false); end);
-- to track new connection
module:hook("resource-bind", function(event)
-- module:log("debug", "resource bind event %s", dumpTable(event.session.resource));
local resbind = event.session.resource;
inactive_devices[resbind] = nil;
end);
module:hook_stanza(xmlns_sm3, "resume", function (event)
-- module:log("debug", "resource bind event %s", dumpTable(event.session.resource));
end);
-- module:hook("pre-iq/full", handle_iq, 1);
module:hook("pre-iq/bare", handle_iq, 100);
-- module:hook("pre-iq/host", handle_iq, 1);
-- module:hook("iq/self/urn:xmpp:vnctalk:fcm", handle_iq, -1);
-- module:hook("iq/bare/urn:xmpp:vnctalk:fcm", handle_iq, -1);
-- module:hook("iq/host/urn:xmpp:vnctalk:fcm", handle_iq, -1);
module:log("info", "FCM notifications ACTIVATED");
end