mod_http_roster_admin/mod_http_roster_admin.lua
author Matthew Wild <mwild1@gmail.com>
Sat, 24 Sep 2022 09:26:26 +0100
changeset 5063 5f1120c284c5
parent 3342 7d2400710d65
permissions -rw-r--r--
mod_cloud_notify_extensions: Add note about dependency Noting here because people might not click through to see it on the mod_cloud_notify_encrypted page.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     1
-- mod_http_roster_admin
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     2
-- Description: Allow user rosters to be sourced from a remote HTTP API
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     3
--
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     4
-- Version: 1.0
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     5
-- Date: 2015-03-06
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     6
-- Author: Matthew Wild <matthew@prosody.im>
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     7
-- License: MPLv2
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     8
--
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
     9
-- Requirements:
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    10
--   Prosody config:
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    11
--     storage = { roster = "memory" }
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    12
--     modules_disabled = { "roster" }
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    13
--   Dependencies:
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    14
--     Prosody 0.9
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    15
--     lua-cjson (Debian/Ubuntu/LuaRocks: lua-cjson)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    16
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    17
local http = require "net.http";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    18
local json = require "cjson";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    19
local it = require "util.iterators";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    20
local set = require "util.set";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    21
local rm = require "core.rostermanager";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    22
local st = require "util.stanza";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    23
local array = require "util.array";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    24
3320
9d8098f4b652 mod_http_roster_admin: Explicitly set 'id' attribute on roster pushes
Matthew Wild <mwild1@gmail.com>
parents: 2635
diff changeset
    25
local new_id = require "util.id".short;
9d8098f4b652 mod_http_roster_admin: Explicitly set 'id' attribute on roster pushes
Matthew Wild <mwild1@gmail.com>
parents: 2635
diff changeset
    26
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    27
local host = module.host;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    28
local sessions = hosts[host].sessions;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    29
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    30
local roster_url = module:get_option_string("http_roster_url", "http://localhost/%s");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    31
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    32
-- Send a roster push to the named user, with the given roster, for the specified
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    33
-- contact's roster entry. Used to notify clients of changes/removals.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    34
local function roster_push(username, roster, contact_jid)
3320
9d8098f4b652 mod_http_roster_admin: Explicitly set 'id' attribute on roster pushes
Matthew Wild <mwild1@gmail.com>
parents: 2635
diff changeset
    35
	local stanza = st.iq({type="set", id=new_id()})
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    36
		:tag("query", {xmlns = "jabber:iq:roster" });
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    37
	local item = roster[contact_jid];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    38
	if item then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    39
		stanza:tag("item", {jid = contact_jid, subscription = item.subscription, name = item.name, ask = item.ask});
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    40
		for group in pairs(item.groups) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    41
			stanza:tag("group"):text(group):up();
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    42
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    43
	else
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    44
		stanza:tag("item", {jid = contact_jid, subscription = "remove"});
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    45
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    46
	stanza:up():up(); -- move out from item
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    47
	for _, session in pairs(hosts[host].sessions[username].sessions) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    48
		if session.interested then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    49
			session.send(stanza);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    50
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    51
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    52
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    53
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    54
-- Send latest presence from the named local user to a contact.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    55
local function send_presence(username, contact_jid, available)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    56
	module:log("debug", "Sending %savailable presence from %s to contact %s", (available and "" or "un"), username, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    57
	for resource, session in pairs(sessions[username].sessions) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    58
		local pres;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    59
		if available then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    60
			pres = st.clone(session.presence);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    61
			pres.attr.to = contact_jid;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    62
		else
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    63
			pres = st.presence({ to = contact_jid, from = session.full_jid, type = "unavailable" });
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    64
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    65
		module:send(pres);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    66
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    67
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    68
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    69
-- Converts a 'friend' object from the API to a Prosody roster item object
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    70
local function friend_to_roster_item(friend)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    71
	return {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    72
		name = friend.name;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    73
		subscription = "both";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    74
		groups = friend.groups or {};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    75
	};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    76
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    77
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    78
-- Returns a handler function to consume the data returned from
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    79
-- the API, compare it to the user's current roster, and perform
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    80
-- any actions necessary (roster pushes, presence probes) to
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    81
-- synchronize them.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    82
local function updated_friends_handler(username, cb)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    83
	return (function (ok, code, friends)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    84
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    85
			cb(false, code);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    86
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    87
		local user = sessions[username];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    88
		local roster = user.roster;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    89
		local old_contacts = set.new(array.collect(it.keys(roster)));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    90
		local new_contacts = set.new(array.collect(it.keys(friends)));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    91
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    92
		-- These two entries are not real contacts, ignore them
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    93
		old_contacts:remove(false);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    94
		old_contacts:remove("pending");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    95
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    96
		module:log("debug", "New friends list of %s: %s", username, json.encode(friends));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    97
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    98
		-- Calculate which contacts have been added/removed since
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    99
		-- the last time we fetched the roster
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   100
		local added_contacts = new_contacts - old_contacts;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   101
		local removed_contacts = old_contacts - new_contacts;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   102
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   103
		local added, removed = 0, 0;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   104
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   105
		-- Add new contacts and notify connected clients
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   106
		for contact_jid in added_contacts do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   107
			module:log("debug", "Processing new friend of %s: %s", username, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   108
			roster[contact_jid] = friend_to_roster_item(friends[contact_jid]);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   109
			roster_push(username, roster, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   110
			send_presence(username, contact_jid, true);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   111
			added = added + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   112
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   113
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   114
		-- Remove contacts and notify connected clients
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   115
		for contact_jid in removed_contacts do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   116
			module:log("debug", "Processing removed friend of %s: %s", username, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   117
			roster[contact_jid] = nil;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   118
			roster_push(username, roster, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   119
			send_presence(username, contact_jid, false);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   120
			removed = removed + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   121
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   122
		module:log("debug", "User %s: added %d new contacts, removed %d contacts", username, added, removed);
2635
2bfa7d476092 mod_http_roster_admin: Don't call callback if it's nil
JC Brand <jc@opkode.com>
parents: 2626
diff changeset
   123
		if cb ~= nil then
2bfa7d476092 mod_http_roster_admin: Don't call callback if it's nil
JC Brand <jc@opkode.com>
parents: 2626
diff changeset
   124
			cb(true);
2bfa7d476092 mod_http_roster_admin: Don't call callback if it's nil
JC Brand <jc@opkode.com>
parents: 2626
diff changeset
   125
		end
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   126
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   127
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   128
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   129
-- Fetch the named user's roster from the API, call callback (cb)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   130
-- with status and result (friends list) when received.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   131
function fetch_roster(username, cb)
2214
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   132
	local x = {headers = {}};
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   133
	x["headers"]["ACCEPT"] = "application/json, text/plain, */*";
2445
68ebc52222dc Log URL called by http_roster_admin
JC Brand <jc@opkode.com>
parents: 2214
diff changeset
   134
	module:log("debug", "Fetching roster at URL: %s", roster_url:format(username));
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   135
	local ok, err = http.request(
2214
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   136
		roster_url:format(username),
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   137
		x,
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   138
		function (roster_data, code)
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   139
			if code ~= 200 then
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   140
				module:log("error", "Error fetching roster from %s (code %d): %s", roster_url:format(username), code, tostring(roster_data):sub(1, 40):match("^[^\r\n]+"));
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   141
				if code ~= 0 then
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   142
					cb(nil, code, roster_data);
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   143
				end
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   144
				return;
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   145
			end
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   146
			module:log("debug", "Successfully fetched roster for %s", username);
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   147
			module:log("debug", "The roster data is %s", roster_data);
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   148
			cb(true, code, json.decode(roster_data));
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   149
		end
126d79bf079b mod_http_roster_admin: Also log if the status error is 0 (Connection refused)
JC Brand <jcbrand@minddistrict.com>
parents: 2165
diff changeset
   150
	);
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   151
	if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   152
		module:log("error", "Failed to connect to roster API at %s: %s", roster_url:format(username), err);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   153
		cb(false, 0, err);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   154
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   155
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   156
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   157
-- Fetch the named user's roster from the API, synchronize it with
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   158
-- the user's current roster. Notify callback (cb) with true/false
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   159
-- depending on success or failure.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   160
function refresh_roster(username, cb)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   161
	local user = sessions[username];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   162
	if not (user and user.roster) then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   163
		module:log("debug", "User's (%q) roster updated, but they are not online - ignoring", username);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   164
		cb(true);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   165
		return;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   166
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   167
	fetch_roster(username, updated_friends_handler(username, cb));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   168
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   169
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   170
--- Roster protocol handling ---
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   171
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   172
-- Build a reply to a "roster get" request
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   173
local function build_roster_reply(stanza, roster_data)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   174
	local roster = st.reply(stanza)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   175
		:tag("query", { xmlns = "jabber:iq:roster" });
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   176
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   177
	for jid, item in pairs(roster_data) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   178
		if jid and jid ~= "pending" then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   179
			roster:tag("item", {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   180
				jid = jid,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   181
				subscription = item.subscription,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   182
				ask = item.ask,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   183
				name = item.name,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   184
			});
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   185
			for group in pairs(item.groups) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   186
				roster:tag("group"):text(group):up();
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   187
			end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   188
			roster:up(); -- move out from item
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   189
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   190
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   191
	return roster;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   192
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   193
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   194
-- Handle clients requesting their roster (generally at login)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   195
-- This will not work if mod_roster is loaded (in 0.9).
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   196
module:hook("iq-get/self/jabber:iq:roster:query", function(event)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   197
	local session, stanza = event.origin, event.stanza;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   198
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   199
	session.interested = true; -- resource is interested in roster updates
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   200
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   201
	local roster = session.roster;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   202
	if roster[false].downloaded then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   203
		return session.send(build_roster_reply(stanza, roster));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   204
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   205
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   206
	-- It's possible that we can call this more than once for a new roster
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   207
	-- Should happen rarely (multiple clients of the same user request the
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   208
	-- roster in the time it takes the API to respond). Currently we just
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   209
	-- issue multiple requests, as it's harmless apart from the wasted
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   210
	-- requests.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   211
	fetch_roster(session.username, function (ok, code, friends)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   212
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   213
			session.send(st.error_reply(stanza, "cancel", "internal-server-error"));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   214
			session:close("internal-server-error");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   215
			return;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   216
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   217
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   218
		-- Are we the first callback to handle the downloaded roster?
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   219
		local first = roster[false].downloaded == nil;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   220
		if first then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   221
			-- Fill out new roster
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   222
			for jid, friend in pairs(friends) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   223
				roster[jid] = friend_to_roster_item(friend);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   224
			end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   225
		end
2621
7c3a1688e385 Purge the roster from RAM when the user logs off.
JC Brand <jc@opkode.com>
parents: 2445
diff changeset
   226
7c3a1688e385 Purge the roster from RAM when the user logs off.
JC Brand <jc@opkode.com>
parents: 2445
diff changeset
   227
		roster[false].downloaded = true;
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   228
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   229
		-- Send full roster to client
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   230
		session.send(build_roster_reply(stanza, roster));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   231
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   232
		if not first then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   233
			-- We already had a roster, make sure to handle any changes...
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   234
			updated_friends_handler(session.username, nil)(ok, code, friends);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   235
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   236
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   237
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   238
	return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   239
end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   240
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   241
-- Prevent client from making changes to the roster. This will not
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   242
-- work if mod_roster is loaded (in 0.9).
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   243
module:hook("iq-set/self/jabber:iq:roster:query", function(event)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   244
	local session, stanza = event.origin, event.stanza;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   245
	return session.send(st.error_reply(stanza, "cancel", "service-unavailable"));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   246
end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   247
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   248
--- HTTP endpoint to trigger roster refresh ---
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   249
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   250
-- Handles updating for a single user: GET /roster_admin/refresh/USERNAME
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   251
function handle_refresh_single(event, username)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   252
	refresh_roster(username, function (ok, code, err)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   253
		event.response.headers["Content-Type"] = "application/json";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   254
		event.response:send(json.encode({
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   255
			status = ok and "ok" or "error";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   256
			message = err or "roster update complete";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   257
		}));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   258
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   259
	return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   260
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   261
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   262
-- Handles updating for multiple users: POST /roster_admin/refresh
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   263
-- Payload should be a JSON array of usernames, e.g. ["user1", "user2", "user3"]
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   264
function handle_refresh_multi(event)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   265
	local users = json.decode(event.request.body);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   266
	if not users then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   267
		module:log("warn", "Multi-user refresh attempted with missing/invalid payload");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   268
		event.response:send(400);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   269
		return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   270
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   271
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   272
	local count, count_err = 0, 0;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   273
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   274
	local function cb(ok)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   275
		count = count + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   276
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   277
			count_err = count_err + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   278
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   279
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   280
		if count == #users then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   281
			event.response.headers["Content-Type"] = "application/json";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   282
			event.response:send(json.encode({
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   283
				status = "ok";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   284
				message = "roster update complete";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   285
				updated = count - count_err;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   286
				errors = count_err;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   287
			}));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   288
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   289
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   290
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   291
	for _, username in ipairs(users) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   292
		refresh_roster(username, cb);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   293
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   294
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   295
	return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   296
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   297
3342
7d2400710d65 mod_http_roster_admin: Add explicit dependency on mod_http
Kim Alvefur <zash@zash.se>
parents: 3320
diff changeset
   298
module:depends("http");
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   299
module:provides("http", {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   300
	route = {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   301
		["POST /refresh"] = handle_refresh_multi;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   302
		["GET /refresh/*"] = handle_refresh_single;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   303
	};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   304
});