Comparison

mod_profile/mod_profile.lua @ 1419:877dc87539ff

mod_profile: Replacement for mod_vcard with vcard4 support and integration with mod_pep_plus
author Kim Alvefur <zash@zash.se>
date Wed, 28 May 2014 22:02:27 +0200
child 1420:808950ab007b
comparison
equal deleted inserted replaced
1418:a0375f84d65a 1419:877dc87539ff
1 -- mod_profile
2
3 local st = require"util.stanza";
4 local jid_split, jid_bare = import("util.jid", "split", "bare");
5 local is_admin = require"core.usermanager".is_admin;
6 local vcard = require"util.vcard";
7 local base64 = require"util.encodings".base64;
8 local sha1 = require"util.hashes".sha1;
9
10 local pep_plus;
11 if module:get_host_type() == "local" and module:get_option_boolean("vcard_to_pep") then
12 pep_plus = module:depends"pep_plus";
13 end
14
15 local storage = module:open_store();
16 local legacy_storage = module:open_store("vcard");
17
18 local function get_item(vcard, name)
19 local item;
20 for i=1, #vcard do
21 item=vcard[i];
22 if item.name == name then
23 return item, i;
24 end
25 end
26 end
27
28 local magic_mime = {
29 ["\137PNG\r\n\026\n"] = "image/png";
30 ["\255\216"] = "image/jpeg";
31 ["GIF87a"] = "image/gif";
32 ["GIF89a"] = "image/gif";
33 ["<?xml"] = "image/svg+xml";
34 }
35 local function identify(data)
36 for magic, mime in pairs(magic_mime) do
37 if data:sub(1, #magic) == magic then
38 return mime;
39 end
40 end
41 return "application/octet-stream";
42 end
43
44 local function update_pep(username, data)
45 local pep = pep_plus.get_pep_service(username.."@"..module.host);
46 if vcard.to_vcard4 then
47 pep:publish("urn:xmpp:vcard4", true, "", st.stanza("item"):add_child(vcard.to_vcard4(data)));
48 end
49
50 local nickname = get_item(data, "NICKNAME");
51 if nickname and nickname[1] then
52 pep:publish("http://jabber.org/protocol/nick", true, "", st.stanza("item")
53 :tag("nick", { xmlns="http://jabber.org/protocol/nick" }):text(nickname[1]));
54 end
55
56 local photo = get_item(data, "PHOTO");
57 if photo and photo[1] then
58 local photo_raw = base64.decode(photo[1]);
59 local photo_hash = sha1(photo_raw, true);
60
61 pep:publish("urn:xmpp:avatar:metadata", true, "", st.stanza("item")
62 :tag("metadata", {
63 xmlns="urn:xmpp:avatar:metadata",
64 bytes = tostring(#photo_raw),
65 id = photo_hash,
66 type = identify(photo_raw),
67 }));
68 pep:publish("urn:xmpp:avatar:data", true, photo_hash, st.stanza("item")
69 :tag("data", { xmlns="urn:xmpp:avatar:data" }):text(photo[1]));
70 end
71 end
72
73 -- The "temporary" vCard XEP-0054 part
74 module:add_feature("vcard-temp");
75
76 local function handle_get(event)
77 local origin, stanza = event.origin, event.stanza;
78 local username = origin.username;
79 local to = stanza.attr.to;
80 if to then username = jid_split(to); end
81 local data = storage:get(username);
82 if not data then
83 data = legacy_storage:get(username);
84 data = data and st.deserialize(data);
85 end
86 if not data then
87 return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
88 end
89 return origin.send(st.reply(stanza):add_child(vcard.to_xep54(data)));
90 end
91
92 local function handle_set(event)
93 local origin, stanza = event.origin, event.stanza;
94 local data = vcard.from_xep54(stanza.tags[1]);
95 local username = origin.username;
96 local to = stanza.attr.to;
97 if to then
98 if not is_admin(jid_bare(stanza.attr.from), module.host) then
99 return origin.send(st.error_reply(stanza, "auth", "forbidden"));
100 end
101 username = jid_split(to);
102 end
103 local ok, err = storage:set(username, data);
104 if not ok then
105 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err));
106 end
107
108 if pep_plus and username then
109 update_pep(username, data);
110 end
111
112 return origin.send(st.reply(stanza));
113 end
114
115 module:hook("iq-get/bare/vcard-temp:vCard", handle_get);
116 module:hook("iq-get/host/vcard-temp:vCard", handle_get);
117
118 module:hook("iq-set/bare/vcard-temp:vCard", handle_set);
119 module:hook("iq-set/host/vcard-temp:vCard", handle_set);
120
121 -- The vCard4 part
122 if vcard.to_vcard4 then
123 module:add_feature("urn:ietf:params:xml:ns:vcard-4.0");
124
125 module:hook("iq-get/bare/urn:ietf:params:xml:ns:vcard-4.0:vcard", function(event)
126 local origin, stanza = event.origin, event.stanza;
127 local username = jid_split(stanza.attr.to) or origin.username;
128 local data = storage:get(username);
129 if not data then
130 return origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
131 end
132 return origin.send(st.reply(stanza):add_child(vcard.to_vcard4(data)));
133 end);
134
135 if vcard.from_vcard4 then
136 module:hook("iq-set/self/urn:ietf:params:xml:ns:vcard-4.0:vcard", function(event)
137 local origin, stanza = event.origin, event.stanza;
138 local ok, err = storage:set(origin.username, vcard.from_vcard4(stanza.tags[1]));
139 if not ok then
140 return origin.send(st.error_reply(stanza, "cancel", "internal-server-error", err));
141 end
142 return origin.send(st.reply(stanza));
143 end);
144 else
145 module:hook("iq-set/self/urn:ietf:params:xml:ns:vcard-4.0:vcard", function(event)
146 local origin, stanza = event.origin, event.stanza;
147 return origin.send(st.error_reply(stanza, "cancel", "feature-not-implemented"));
148 end);
149 end
150 end
151