2 -- Copyright (C) 2008-2014 Matthew Wild
3 -- Copyright (C) 2008-2014 Waqas Hussain
4 -- Copyright (C) 2014 Kim Alvefur
6 -- This project is MIT/X11 licensed. Please see the
7 -- COPYING file in the source package for more information.
10 local st
= require
"util.stanza"
11 local jid
= require
"util.jid";
12 local base64
= require
"util.encodings".base64
;
13 local sha1
= require
"util.hashes".sha1
;
15 local mm
= require
"core.modulemanager";
18 local pep_module_name
= "pep";
19 if mm
.get_modules_for_host
then
20 if mm
.get_modules_for_host(module
.host
):contains("pep_simple") then
21 pep_module_name
= "pep_simple";
25 local mod_pep
= module
:depends(pep_module_name
);
26 local pep_data
= mod_pep
.module
.save().data
;
29 module
:log("error", "This module is not compatible with your version of mod_pep");
30 if mm
.get_modules_for_host
then
31 module
:log("error", "Please use mod_pep_simple instead of mod_pep to continue using this module");
36 module
:add_feature("http://prosody.im/protocol/vcard-pep-integration");
37 module
:depends
"vcard";
38 local vcard_storage
= module
:open_store("vcard");
40 local function get_vcard(username
)
41 local vcard
, err
= vcard_storage
:get(username
);
43 vcard
= st
.deserialize(vcard
);
46 vcard
= st
.stanza("vCard", { xmlns
= "vcard-temp" });
51 local function replace_tag(s
, replacement
)
53 s
:maptags(function (tag)
54 if tag.name
== replacement
.name
and tag.attr
.xmlns
== replacement
.attr
.xmlns
then
65 s
:add_child(replacement
);
69 local function set_vcard(username
, vcard
)
71 vcard
= st
.preserialize(st
.clone(vcard
));
73 return vcard_storage
:set(username
, vcard
);
76 local function publish(session
, node
, id
, item
)
77 return module
:fire_event("pep-publish-item", {
78 actor
= true, user
= jid
.bare(session
.full_jid
), session
= session
, node
= node
, id
= id
, item
= item
;
83 local function update_pep(session
, vcard
)
84 if not vcard
then return end
85 local nickname
= vcard
:get_child_text("NICKNAME");
87 publish(session
, "http://jabber.org/protocol/nick", "current", st
.stanza("item", {id
="current"})
88 :tag("nick", { xmlns
="http://jabber.org/protocol/nick" }):text(nickname
));
91 local photo
= vcard
:get_child("PHOTO");
93 local photo_type
= photo
:get_child_text("TYPE");
94 local photo_b64
= photo
:get_child_text("BINVAL");
95 local photo_raw
= photo_b64
and base64
.decode(photo_b64
);
96 if photo_raw
and photo_type
then -- Else invalid data or encoding
97 local photo_hash
= sha1(photo_raw
, true);
99 publish(session
, "urn:xmpp:avatar:data", photo_hash
, st
.stanza("item", {id
=photo_hash
})
100 :tag("data", { xmlns
="urn:xmpp:avatar:data" }):text(photo_b64
));
101 publish(session
, "urn:xmpp:avatar:metadata", photo_hash
, st
.stanza("item", {id
=photo_hash
})
102 :tag("metadata", { xmlns
="urn:xmpp:avatar:metadata" })
103 :tag("info", { id
= photo_hash
, bytes
= tostring(#photo_raw
), type = photo_type
,}));
108 local function handle_vcard(event
)
109 local session
, stanza
= event
.origin
, event
.stanza
;
110 if not stanza
.attr
.to
and stanza
.attr
.type == "set" then
111 return update_pep(session
, stanza
:get_child("vCard", "vcard-temp"));
115 module
:hook("iq/bare/vcard-temp:vCard", handle_vcard
, 1);
117 -- PEP Avatar -> vCard
118 local function on_publish_metadata(event
)
119 local username
= event
.session
.username
;
120 local metadata
= event
.item
:find("{urn:xmpp:avatar:metadata}metadata/info");
122 module
:log("error", "No info found");
123 module
:log("debug", event
.item
:top_tag());
126 module
:log("debug", metadata
:top_tag());
127 local user_data
= pep_data
[username
.."@"..module
.host
];
128 local pep_photo
= user_data
["urn:xmpp:avatar:data"];
129 pep_photo
= pep_photo
and pep_photo
[1] == metadata
.attr
.id
and pep_photo
[2];
130 if not pep_photo
then
131 module
:log("error", "No photo found");
133 end -- Publishing in the wrong order?
134 local image
=pep_photo
:get_child_text("data", "urn:xmpp:avatar:data");
135 if pep_photo
and metadata
.attr
.type == "image/webp" then
136 local file_webp
= io
.open("/tmp/Prosody_temp_avatar.webp", "w");
137 file_webp
:write(base64
.decode(pep_photo
:get_child_text("data", "urn:xmpp:avatar:data")));
139 os
.execute("dwebp /tmp/Prosody_temp_avatar.webp -o /tmp/Prosody_temp_avatar.png");
140 local file_png
= io
.open("/tmp/Prosody_temp_avatar.png", "r");
141 if file_png
~= nil then
142 image
=base64
.encode(file_png
:read("*a"));
145 module
:log("error", "Couldn't access /tmp/Prosody_temp_avatar.png. Are you sure that /tmp is readable and writable and that Prosody can execute the dwebp command?");
147 os
.remove("/tmp/Prosody_temp_avatar.webp");
148 os
.remove("/tmp/Prosody_temp_avatar.png");
150 local vcard
= get_vcard(username
);
151 local new_photo
= st
.stanza("PHOTO", { xmlns
= "vcard-temp" })
152 :tag("TYPE"):text(metadata
.attr
.type):up()
153 :tag("BINVAL"):text(image
);
155 replace_tag(vcard
, new_photo
);
156 set_vcard(username
, vcard
);
159 -- PEP Nickname -> vCard
160 local function on_publish_nick(event
)
161 local username
= event
.session
.username
;
162 local vcard
= get_vcard(username
);
163 local new_nick
= st
.stanza("NICKNAME", { xmlns
= "vcard-temp" })
164 :text(event
.item
:get_child_text("nick", "http://jabber.org/protocol/nick"));
165 replace_tag(vcard
, new_nick
);
166 set_vcard(username
, vcard
);
169 local function on_publish(event
)
170 if event
.actor
== true then return end -- Not from a client
171 local node
= event
.node
;
172 if node
== "urn:xmpp:avatar:metadata" then
173 return on_publish_metadata(event
);
174 elseif node
== "http://jabber.org/protocol/nick" then
175 return on_publish_nick(event
);
179 module
:hook("pep-publish-item", on_publish
, 1);