Mercurial > prosody-modules
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 |
| parents | |
| children | 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 |
