mod_http_roster_admin/mod_http_roster_admin.lua
author Kim Alvefur <zash@zash.se>
Sun, 04 Feb 2018 16:00:51 +0100
changeset 2878 178b8abf8283
parent 2635 2bfa7d476092
child 3320 9d8098f4b652
permissions -rw-r--r--
mod_sms_clickatell: Fix typo in comment [codespell]
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
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    25
local host = module.host;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    26
local sessions = hosts[host].sessions;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    27
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    28
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
    29
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    30
-- 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
    31
-- 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
    32
local function roster_push(username, roster, contact_jid)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    33
	local stanza = st.iq({type="set"})
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    34
		:tag("query", {xmlns = "jabber:iq:roster" });
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    35
	local item = roster[contact_jid];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    36
	if item then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    37
		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
    38
		for group in pairs(item.groups) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    39
			stanza:tag("group"):text(group):up();
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    40
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    41
	else
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    42
		stanza:tag("item", {jid = contact_jid, subscription = "remove"});
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    43
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    44
	stanza:up():up(); -- move out from item
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    45
	for _, session in pairs(hosts[host].sessions[username].sessions) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    46
		if session.interested then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    47
			session.send(stanza);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    48
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    49
	end
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
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    52
-- 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
    53
local function send_presence(username, contact_jid, available)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    54
	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
    55
	for resource, session in pairs(sessions[username].sessions) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    56
		local pres;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    57
		if available then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    58
			pres = st.clone(session.presence);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    59
			pres.attr.to = contact_jid;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    60
		else
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    61
			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
    62
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    63
		module:send(pres);
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
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    66
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    67
-- 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
    68
local function friend_to_roster_item(friend)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    69
	return {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    70
		name = friend.name;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    71
		subscription = "both";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    72
		groups = friend.groups or {};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    73
	};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    74
end
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
-- Returns a handler function to consume the data returned from
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    77
-- 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
    78
-- any actions necessary (roster pushes, presence probes) to
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    79
-- synchronize them.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    80
local function updated_friends_handler(username, cb)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    81
	return (function (ok, code, friends)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    82
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    83
			cb(false, code);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    84
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    85
		local user = sessions[username];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    86
		local roster = user.roster;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    87
		local old_contacts = set.new(array.collect(it.keys(roster)));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    88
		local new_contacts = set.new(array.collect(it.keys(friends)));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    89
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    90
		-- These two entries are not real contacts, ignore them
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    91
		old_contacts:remove(false);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    92
		old_contacts:remove("pending");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    93
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    94
		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
    95
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    96
		-- Calculate which contacts have been added/removed since
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    97
		-- the last time we fetched the roster
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    98
		local added_contacts = new_contacts - old_contacts;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
    99
		local removed_contacts = old_contacts - new_contacts;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   100
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   101
		local added, removed = 0, 0;
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
		-- Add new contacts and notify connected clients
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   104
		for contact_jid in added_contacts do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   105
			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
   106
			roster[contact_jid] = friend_to_roster_item(friends[contact_jid]);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   107
			roster_push(username, roster, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   108
			send_presence(username, contact_jid, true);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   109
			added = added + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   110
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   111
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   112
		-- Remove contacts and notify connected clients
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   113
		for contact_jid in removed_contacts do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   114
			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
   115
			roster[contact_jid] = nil;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   116
			roster_push(username, roster, contact_jid);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   117
			send_presence(username, contact_jid, false);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   118
			removed = removed + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   119
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   120
		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
   121
		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
   122
			cb(true);
2bfa7d476092 mod_http_roster_admin: Don't call callback if it's nil
JC Brand <jc@opkode.com>
parents: 2626
diff changeset
   123
		end
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   124
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   125
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   126
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   127
-- 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
   128
-- with status and result (friends list) when received.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   129
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
   130
	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
   131
	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
   132
	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
   133
	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
   134
		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
   135
		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
   136
		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
   137
			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
   138
				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
   139
				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
   140
					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
   141
				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
   142
				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
   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
			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
   145
			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
   146
			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
   147
		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
   148
	);
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   149
	if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   150
		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
   151
		cb(false, 0, err);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   152
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   153
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   154
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   155
-- 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
   156
-- 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
   157
-- depending on success or failure.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   158
function refresh_roster(username, cb)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   159
	local user = sessions[username];
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   160
	if not (user and user.roster) then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   161
		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
   162
		cb(true);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   163
		return;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   164
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   165
	fetch_roster(username, updated_friends_handler(username, cb));
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
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   168
--- Roster protocol handling ---
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
-- Build a reply to a "roster get" request
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   171
local function build_roster_reply(stanza, roster_data)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   172
	local roster = st.reply(stanza)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   173
		:tag("query", { xmlns = "jabber:iq:roster" });
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   174
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   175
	for jid, item in pairs(roster_data) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   176
		if jid and jid ~= "pending" then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   177
			roster:tag("item", {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   178
				jid = jid,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   179
				subscription = item.subscription,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   180
				ask = item.ask,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   181
				name = item.name,
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   182
			});
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   183
			for group in pairs(item.groups) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   184
				roster:tag("group"):text(group):up();
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   185
			end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   186
			roster:up(); -- move out from item
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
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   189
	return roster;
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
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   192
-- Handle clients requesting their roster (generally at login)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   193
-- 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
   194
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
   195
	local session, stanza = event.origin, event.stanza;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   196
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   197
	session.interested = true; -- resource is interested in roster updates
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
	local roster = session.roster;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   200
	if roster[false].downloaded then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   201
		return session.send(build_roster_reply(stanza, roster));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   202
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   203
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   204
	-- 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
   205
	-- 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
   206
	-- 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
   207
	-- 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
   208
	-- requests.
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   209
	fetch_roster(session.username, function (ok, code, friends)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   210
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   211
			session.send(st.error_reply(stanza, "cancel", "internal-server-error"));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   212
			session:close("internal-server-error");
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   213
			return;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   214
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   215
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   216
		-- Are we the first callback to handle the downloaded roster?
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   217
		local first = roster[false].downloaded == nil;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   218
		if first then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   219
			-- Fill out new roster
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   220
			for jid, friend in pairs(friends) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   221
				roster[jid] = friend_to_roster_item(friend);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   222
			end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   223
		end
2621
7c3a1688e385 Purge the roster from RAM when the user logs off.
JC Brand <jc@opkode.com>
parents: 2445
diff changeset
   224
7c3a1688e385 Purge the roster from RAM when the user logs off.
JC Brand <jc@opkode.com>
parents: 2445
diff changeset
   225
		roster[false].downloaded = true;
2165
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   226
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   227
		-- Send full roster to client
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   228
		session.send(build_roster_reply(stanza, roster));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   229
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   230
		if not first then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   231
			-- 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
   232
			updated_friends_handler(session.username, nil)(ok, code, friends);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   233
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   234
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   235
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   236
	return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   237
end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   238
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   239
-- 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
   240
-- work if mod_roster is loaded (in 0.9).
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   241
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
   242
	local session, stanza = event.origin, event.stanza;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   243
	return session.send(st.error_reply(stanza, "cancel", "service-unavailable"));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   244
end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   245
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   246
--- HTTP endpoint to trigger roster refresh ---
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
-- 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
   249
function handle_refresh_single(event, username)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   250
	refresh_roster(username, function (ok, code, err)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   251
		event.response.headers["Content-Type"] = "application/json";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   252
		event.response:send(json.encode({
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   253
			status = ok and "ok" or "error";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   254
			message = err or "roster update complete";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   255
		}));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   256
	end);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   257
	return true;
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
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   260
-- Handles updating for multiple users: POST /roster_admin/refresh
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   261
-- 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
   262
function handle_refresh_multi(event)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   263
	local users = json.decode(event.request.body);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   264
	if not users then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   265
		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
   266
		event.response:send(400);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   267
		return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   268
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   269
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   270
	local count, count_err = 0, 0;
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 function cb(ok)
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   273
		count = count + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   274
		if not ok then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   275
			count_err = count_err + 1;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   276
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   277
		
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   278
		if count == #users then
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   279
			event.response.headers["Content-Type"] = "application/json";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   280
			event.response:send(json.encode({
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   281
				status = "ok";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   282
				message = "roster update complete";
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   283
				updated = count - count_err;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   284
				errors = count_err;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   285
			}));
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   286
		end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   287
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   288
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   289
	for _, username in ipairs(users) do
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   290
		refresh_roster(username, cb);
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   291
	end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   292
	
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   293
	return true;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   294
end
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   295
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   296
module:provides("http", {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   297
	route = {
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   298
		["POST /refresh"] = handle_refresh_multi;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   299
		["GET /refresh/*"] = handle_refresh_single;
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   300
	};
95a9f2d234da Add mod_http_roster_admin
JC Brand <jc@opkode.com>
parents:
diff changeset
   301
});