Software /
code /
prosody-modules
Comparison
mod_service_directories/mod_service_directories.lua @ 552:d1e83cb12885
mod_service_directories: Initial commit. Untested. Support for both directory and buddy use cases. Incomplete pubsub support (only getting all items supported).
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Sun, 15 Jan 2012 02:56:51 +0500 |
child | 759:6531a029fce5 |
comparison
equal
deleted
inserted
replaced
551:859bf77b9fbf | 552:d1e83cb12885 |
---|---|
1 -- Prosody IM | |
2 -- Copyright (C) 2011 Waqas Hussain | |
3 -- | |
4 -- This project is MIT/X11 licensed. Please see the | |
5 -- COPYING file in the source package for more information. | |
6 -- | |
7 | |
8 -- An implementation of [XEP-0309: Service Directories] | |
9 | |
10 -- Imports and defines | |
11 | |
12 local st = require "util.stanza"; | |
13 local jid_split = require "util.jid".split; | |
14 local adhoc_new = module:require "adhoc".new; | |
15 local to_ascii = require "util.encodings".idna.to_ascii; | |
16 local nameprep = require "util.encodings".stringprep.nameprep; | |
17 local core_post_stanza = core_post_stanza; | |
18 local pairs, ipairs = pairs, ipairs; | |
19 local module = module; | |
20 local hosts = hosts; | |
21 | |
22 local subscription_from = {}; | |
23 local subscription_to = {}; | |
24 local contact_features = {}; | |
25 local contact_vcards = {}; | |
26 | |
27 -- Advertise in disco | |
28 | |
29 module:add_identity("server", "directory", "Prosody"); | |
30 module:add_feature("urn:xmpp:server-presence"); | |
31 | |
32 -- Handle subscriptions | |
33 | |
34 module:hook("presence/host", function(event) -- inbound presence to the host | |
35 local origin, stanza = event.origin, event.stanza; | |
36 | |
37 local node, host, resource = jid_split(stanza.attr.from); | |
38 if stanza.attr.from ~= host then return; end -- not from a host | |
39 | |
40 local t = stanza.attr.type; | |
41 if t == "probe" then | |
42 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = stanza.attr.id })); | |
43 elseif t == "subscribe" then | |
44 subscription_from[host] = true; | |
45 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = stanza.attr.id, type = "subscribed" })); | |
46 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = stanza.attr.id })); | |
47 add_contact(host); | |
48 elseif t == "subscribed" then | |
49 subscription_to[host] = true; | |
50 query_host(host); | |
51 elseif t == "unsubscribe" then | |
52 subscription_from[host] = nil; | |
53 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = stanza.attr.id, type = "unsubscribed" })); | |
54 remove_contact(host); | |
55 elseif t == "unsubscribed" then | |
56 subscription_to[host] = nil; | |
57 remove_contact(host); | |
58 end | |
59 return true; | |
60 end, 10); -- priority over mod_presence | |
61 | |
62 function remove_contact(host, id) | |
63 contact_features[host] = nil; | |
64 contact_vcards[host] = nil; | |
65 if subscription_to[host] then | |
66 subscription_to[host] = nil; | |
67 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = id, type = "unsubscribe" })); | |
68 end | |
69 if subscription_from[host] then | |
70 subscription_from[host] = nil; | |
71 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = id, type = "unsubscribed" })); | |
72 end | |
73 end | |
74 function add_contact(host, id) | |
75 if not subscription_to[host] then | |
76 core_post_stanza(hosts[module.host], st.presence({ from = module.host, to = host, id = id, type = "subscribe" })); | |
77 end | |
78 end | |
79 | |
80 -- Admin ad-hoc command to subscribe | |
81 | |
82 local function add_contact_handler(self, data, state) | |
83 local layout = { | |
84 title = "Adding a Server Buddy"; | |
85 instructions = "Fill out this form to add a \"server buddy\"."; | |
86 | |
87 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" }; | |
88 { name = "peerjid", type = "jid-single", required = true, label = "The server to add" }; | |
89 }; | |
90 | |
91 if not state then | |
92 return { status = "executing", form = layout }, "executing"; | |
93 elseif data.action == "canceled" then | |
94 return { status = "canceled" }; | |
95 else | |
96 local fields = layout:data(data); | |
97 local peerjid = nameprep(fields.peerjid); | |
98 if not peerjid or peerjid == "" or #peerjid > 1023 or not to_ascii(peerjid) then | |
99 return { status = "completed", error = { message = "Invalid JID" } }; | |
100 end | |
101 add_contact(peerjid); | |
102 return { status = "completed" }; | |
103 end | |
104 end | |
105 | |
106 local add_contact_command = adhoc_new("Adding a Server Buddy", "http://jabber.org/protocol/admin#server-buddy", add_contact_handler, "admin"); | |
107 module:add_item("adhoc", add_contact_command); | |
108 | |
109 -- Disco query remote host | |
110 function query_host(host) | |
111 local stanza = st.iq({ from = module.host, to = host, type = "get", id = "mod_service_directories:disco" }) | |
112 :query("http://jabber.org/protocol/disco#info"); | |
113 core_post_stanza(hosts[module.host], stanza); | |
114 end | |
115 | |
116 -- Handle disco query result | |
117 module:hook("iq-result/bare/mod_service_directories:disco", function(event) | |
118 local origin, stanza = event.origin, event.stanza; | |
119 | |
120 if not subscription_to[stanza.attr.from] then return; end -- not from a contact | |
121 local host = stanza.attr.from; | |
122 | |
123 local query = stanza:get_child("query", "http://jabber.org/protocol/disco#info") | |
124 if not query then return; end | |
125 | |
126 -- extract disco features | |
127 local features = {}; | |
128 for _,tag in ipairs(query.tags) do | |
129 if tag.name == "feature" and tag.attr.var then | |
130 features[tag.attr.var] = true; | |
131 end | |
132 end | |
133 contact_features[host] = features; | |
134 | |
135 if features["urn:ietf:params:xml:ns:vcard-4.0"] then | |
136 local stanza = st.iq({ from = module.host, to = host, type = "get", id = "mod_service_directories:vcard" }) | |
137 :tag("vcard", { xmlns = "urn:ietf:params:xml:ns:vcard-4.0" }); | |
138 core_post_stanza(hosts[module.host], stanza); | |
139 end | |
140 return true; | |
141 end); | |
142 | |
143 -- Handle vcard result | |
144 module:hook("iq-result/bare/mod_service_directories:vcard", function(event) | |
145 local origin, stanza = event.origin, event.stanza; | |
146 | |
147 if not subscription_to[stanza.attr.from] then return; end -- not from a contact | |
148 local host = stanza.attr.from; | |
149 | |
150 local vcard = stanza:get_child("vcard", "urn:ietf:params:xml:ns:vcard-4.0"); | |
151 if not vcard then return; end | |
152 | |
153 contact_vcards[host] = st.clone(vcard); | |
154 return true; | |
155 end); | |
156 | |
157 -- PubSub | |
158 | |
159 -- TODO the following should be replaced by mod_pubsub | |
160 | |
161 module:hook("iq-get/host/http://jabber.org/protocol/pubsub:pubsub", function(event) | |
162 local origin, stanza = event.origin, event.stanza; | |
163 local payload = stanza.tags[1]; | |
164 | |
165 local items = payload:get_child("items", "http://jabber.org/protocol/pubsub"); | |
166 if items and items.attr.node == "urn:xmpp:contacts" then | |
167 local reply = st.reply(stanza) | |
168 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | |
169 :tag("items", { node = "urn:xmpp:contacts" }); | |
170 for host, vcard in pairs(contact_vcards) do | |
171 reply:tag("item", { id = host }) | |
172 :add_child(vcard) | |
173 :up(); | |
174 end | |
175 origin.send(reply); | |
176 return true; | |
177 end | |
178 end); | |
179 |