clix/avatar.lua

changeset 157
aa0f11fb166c
child 168
75e8ca131178
equal deleted inserted replaced
156:3369ae4ff520 157:aa0f11fb166c
1 local b64 = require"util.encodings".base64.encode;
2 local unb64 = require"util.encodings".base64.decode;
3 local st = require "util.stanza";
4 local sha1 = require "util.hashes".sha1;
5
6 return function(opts, arg)
7 if opts.short_help then
8 print("Manage PEP avatars");
9 return;
10 end
11
12 local subcommands = {};
13
14 function subcommands.fetch(conn)
15 local waiting = {[true] = true};
16 for _, userjid in ipairs(arg) do
17 waiting[userjid] = true;
18 local userpep = conn.pubsub:service(userjid);
19 userpep:node("urn:xmpp:avatar:metadata"):items(true, function(result)
20 local metadata_tag = result:find("{http://jabber.org/protocol/pubsub}pubsub/items/item/{urn:xmpp:avatar:metadata}metadata");
21 if not metadata_tag or not metadata_tag:get_child("info") then
22 if result.attr.type == "error" then
23 conn:error("Got error from %s: %s:%s", userjid, result:get_error());
24 else
25 conn:error("%s has no avatar", userjid)
26 end
27 waiting[userjid] = nil;
28 if next(waiting) == nil then
29 conn:close();
30 end
31 return;
32 end
33
34 for info_tag in metadata_tag:childtags("info") do
35 conn:info("Has avatar with type %s", info_tag.attr.type or "?")
36 local filename = (opts.output or (userjid .. "_" .. info_tag.attr.id)) .. "." .. (info_tag.attr.type or "/dat"):match("/([^./+]+)");
37 local output = assert(io.open(filename, "w"));
38 waiting[info_tag.attr.id] = true;
39 conn:debug("Writing to %s", filename);
40 userpep:node("urn:xmpp:avatar:data"):item(info_tag.attr.id, function(dataresult)
41 local data = unb64(dataresult:find("{http://jabber.org/protocol/pubsub}pubsub/items/item/{urn:xmpp:avatar:data}data#"));
42 if data then
43 assert(output:write(data));
44 assert(output:close());
45 conn:info("Avatar of %s written to %s", userjid, filename);
46 else
47 conn:error("Got no data for %s id %s", userjid, info_tag.attr.id);
48 end
49 waiting[info_tag.attr.id] = nil;
50 if next(waiting) == nil then
51 conn:close();
52 end
53 end);
54
55 if not opts.all then
56 break
57 end
58 end
59
60 waiting[userjid] = nil;
61 end)
62 end
63 waiting[true] = nil;
64 end
65
66 function subcommands.publish(conn)
67 local waiting = {meta=true};
68 local userpep = conn.pubsub:service(nil);
69 local metadata_tag = st.stanza("metadata", { xmlns = "urn:xmpp:avatar:metadata" });
70 local metadata_node = userpep:node("urn:xmpp:avatar:metadata");
71 local data_node = userpep:node("urn:xmpp:avatar:data");
72 local first_h = nil;
73 local sha1pat = string.rep("%x", #sha1("",true));
74
75 for _, file in ipairs(arg) do
76 local h, width, height, typ = file:match("_("..sha1pat..")_(%d+)x(%d+)%.(%w+)$");
77 if not h then h, typ = file:match("_("..sha1pat..")%.(%w+)$"); end
78 if not h then typ = file:match("%.(%w+)$"); end
79
80 local f = assert(io.open(file));
81 local data = f:read("*a");
82 f:close();
83 local bytes = string.format("%d", #data);
84
85 if not h then h = sha1(data, true); end
86 if not first_h then first_h = h; end
87 if typ == "jpg" then typ = "jpeg"; end
88
89 local data_tag = st.stanza("data", { xmlns = "urn:xmpp:avatar:data" }):text(b64(data));
90 waiting[h] = true;
91 data_node:publish(h, nil, data_tag, function(ok)
92 waiting[h] = nil;
93 if next(waiting) == nil then
94 conn:close();
95 end
96
97 end);
98
99 metadata_tag:tag("info", {id = h; type = "image/" .. typ; bytes = bytes; width = width; height = height}):up();
100 end
101
102 metadata_node:publish(first_h, nil, metadata_tag, function(ok)
103 waiting.meta = nil;
104 if next(waiting) == nil then
105 conn:close();
106 end
107 end);
108
109 end
110
111 if ((#arg == 0) or opts.help) then
112 print("Subcommands:");
113 print(" publish file_HASH_WxH.png");
114 print(" fetch user@example.com");
115 return 0;
116 end
117 if opts.output and opts.all then
118 print("Can't download multiple avatars to a single file")
119 return 1;
120 end
121
122 local subcommand = table.remove(arg, 1);
123 local on_connect = subcommands[subcommand];
124
125 if not on_connect then
126 print("No such command: " .. subcommand);
127 return 1;
128 end
129
130 return clix_connect(opts, on_connect, {"pubsub"})
131 end

mercurial