plugins/compression.lua

Thu, 17 Mar 2011 18:33:52 +0100

author
Kim Alvefur <zash@zash.se>
date
Thu, 17 Mar 2011 18:33:52 +0100
changeset 197
7e98cf2c1d8d
parent 176
6004486e8b6c
child 250
a5ac643a7fd6
permissions
-rw-r--r--

plugins.*: Use verse.stanza() & co instead of require util.stanza

71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1 -- Copyright (C) 2009-2010 Matthew Wild
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
2 -- Copyright (C) 2009-2010 Tobias Markmann
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
3 --
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
4 -- This project is MIT/X11 licensed. Please see the
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
5 -- COPYING file in the source package for more information.
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
6 --
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
7
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
8 local zlib = require "zlib";
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
9
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
10 local xmlns_compression_feature = "http://jabber.org/features/compress"
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
11 local xmlns_compression_protocol = "http://jabber.org/protocol/compress"
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
12 local xmlns_stream = "http://etherx.jabber.org/streams";
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
13
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
14 local compression_level = 9;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
15
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
16 -- returns either nil or a fully functional ready to use inflate stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
17 local function get_deflate_stream(session)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
18 local status, deflate_stream = pcall(zlib.deflate, compression_level);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
19 if status == false then
197
7e98cf2c1d8d plugins.*: Use verse.stanza() & co instead of require util.stanza
Kim Alvefur <zash@zash.se>
parents: 176
diff changeset
20 local error_st = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
21 session:send(error_st);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
22 session:error("Failed to create zlib.deflate filter: %s", tostring(deflate_stream));
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
23 return
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
24 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
25 return deflate_stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
26 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
27
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
28 -- returns either nil or a fully functional ready to use inflate stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
29 local function get_inflate_stream(session)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
30 local status, inflate_stream = pcall(zlib.inflate);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
31 if status == false then
197
7e98cf2c1d8d plugins.*: Use verse.stanza() & co instead of require util.stanza
Kim Alvefur <zash@zash.se>
parents: 176
diff changeset
32 local error_st = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("setup-failed");
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
33 session:send(error_st);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
34 session:error("Failed to create zlib.inflate filter: %s", tostring(inflate_stream));
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
35 return
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
36 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
37 return inflate_stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
38 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
39
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
40 -- setup compression for a stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
41 local function setup_compression(session, deflate_stream)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
42 function session:send(t)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
43 --TODO: Better code injection in the sending process
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
44 local status, compressed, eof = pcall(deflate_stream, tostring(t), 'sync');
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
45 if status == false then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
46 session:close({
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
47 condition = "undefined-condition";
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
48 text = compressed;
197
7e98cf2c1d8d plugins.*: Use verse.stanza() & co instead of require util.stanza
Kim Alvefur <zash@zash.se>
parents: 176
diff changeset
49 extra = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("processing-failed");
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
50 });
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
51 session:warn("Compressed send failed: %s", tostring(compressed));
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
52 return;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
53 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
54 session.conn:write(compressed);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
55 end;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
56 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
57
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
58 -- setup decompression for a stream
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
59 local function setup_decompression(session, inflate_stream)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
60 local old_data = session.data
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
61 session.data = function(conn, data)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
62 session:debug("Decompressing data...");
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
63 local status, decompressed, eof = pcall(inflate_stream, data);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
64 if status == false then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
65 session:close({
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 condition = "undefined-condition";
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67 text = decompressed;
197
7e98cf2c1d8d plugins.*: Use verse.stanza() & co instead of require util.stanza
Kim Alvefur <zash@zash.se>
parents: 176
diff changeset
68 extra = verse.stanza("failure", {xmlns=xmlns_compression_protocol}):tag("processing-failed");
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
69 });
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
70 stream:warn("%s", tostring(decompressed));
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 return;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 return old_data(conn, decompressed);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
74 end;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
75 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
76
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77 function verse.plugins.compression(stream)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78 local function handle_features(features)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
79 if not stream.compressed then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80 -- does remote server support compression?
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
81 local comp_st = features:child_with_name("compression");
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
82 if comp_st then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
83 -- do we support the mechanism
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
84 for a in comp_st:children() do
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
85 local algorithm = a[1]
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
86 if algorithm == "zlib" then
197
7e98cf2c1d8d plugins.*: Use verse.stanza() & co instead of require util.stanza
Kim Alvefur <zash@zash.se>
parents: 176
diff changeset
87 stream:send(verse.stanza("compress", {xmlns=xmlns_compression_protocol}):tag("method"):text("zlib"))
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
88 stream:debug("Enabled compression using zlib.")
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
89 return true;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
90 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
91 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
92 session:debug("Remote server supports no compression algorithm we support.")
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
93 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
94 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
95 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
96 local function handle_compressed(stanza)
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
97 if stanza.name == "compressed" then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
98 stream:debug("Activating compression...")
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
99
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
100 -- create deflate and inflate streams
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
101 local deflate_stream = get_deflate_stream(stream);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
102 if not deflate_stream then return end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
103
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
104 local inflate_stream = get_inflate_stream(stream);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
105 if not inflate_stream then return end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
106
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
107 -- setup compression for stream.w
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
108 setup_compression(stream, deflate_stream);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
109
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
110 -- setup decompression for stream.data
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
111 setup_decompression(stream, inflate_stream);
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
112
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
113 stream.compressed = true;
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
114 stream:reopen();
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
115 elseif stanza.name == "failure" then
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
116 stream:warn("Failed to establish compression");
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
117 end
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
118 end
72
1649a6022adb verse.plugins.compression: Bump priority of stream-features handler to negotiate compression before resource binding
Matthew Wild <mwild1@gmail.com>
parents: 71
diff changeset
119 stream:hook("stream-features", handle_features, 250);
71
5b74d155036d verse.plugins.compression: Support for stream compression (requires brimworks lua-zlib)
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
120 stream:hook("stream/"..xmlns_compression_protocol, handle_compressed);
79
da06d4996992 plugins.compression: Add newline at end of file (helps old versions of squish for one thing)
Matthew Wild <mwild1@gmail.com>
parents: 74
diff changeset
121 end

mercurial