verse.lua

changeset 88
98b4cf14960b
parent 12
fc435d567896
child 103
9e0e56393978
--- a/verse.lua	Sun May 07 07:50:45 2017 +0100
+++ b/verse.lua	Sun May 07 07:51:07 2017 +0100
@@ -2,123 +2,170 @@
 local function e()
 error("Function not implemented");
 end
-local t=require"mime";
-module"encodings"
+local e=require"mime";
+return{
 stringprep={};
-base64={encode=t.b64,decode=e};
-return _M;
+base64={encode=e.b64,decode=e.unb64};
+};
 end)
 package.preload['util.hashes']=(function(...)
-local e=require"util.sha1";
-return{sha1=e.sha1};
+local t,e=pcall(require,"crypto");
+if t then
+local t={};
+local o=e.digest;
+local function s(e)
+return function(t,a)
+return o(e,t,not a);
+end
+end
+local e=e.hmac.digest;
+local function n(o)
+return function(i,a,t)
+return e(o,a,i,not t);
+end
+end
+local e={"md5","sha1","sha256","sha512"};
+for a,e in ipairs(e)do
+t[e]=s(e);
+t["hmac_"..e]=n(e);
+end
+return t;
+else
+local t=require"util.sha1".sha1;
+local s=require"bit".bxor;
+local a=string.rep;
+local n=string.char;
+local d=string.byte;
+local i=table.concat;
+local function r(e,h,r)
+if#e>64 then
+e=t(e);
+elseif#e<64 then
+e=e..a("\0",64-#e);
+end
+local o,a={},{}
+for t=1,64 do
+local e=d(e,t)
+o[t]=n(s(e,92));
+a[t]=n(s(e,54));
+end
+o=i(o);
+a=i(a);
+return t(o..t(a..h),r);
+end
+return{
+sha1=t;
+hmac_sha1=r;
+};
+end
 end)
 package.preload['util.sha1']=(function(...)
-local l=string.len
-local a=string.char
-local g=string.byte
+local r=string.len
+local o=string.char
+local j=string.byte
 local q=string.sub
-local u=math.floor
+local h=math.floor
 local t=require"bit"
-local j=t.bnot
+local g=t.bnot
 local e=t.band
 local y=t.bor
 local n=t.bxor
-local o=t.lshift
+local a=t.lshift
 local i=t.rshift
-local s,h,d,r,f
+local m,d,c,l,u
 local function p(e,t)
-return o(e,t)+i(e,32-t)
-end
-local function m(i)
-local t,o
+return a(e,t)+i(e,32-t)
+end
+local function s(i)
+local t,a
 local t=""
 for n=1,8 do
-o=e(i,15)
-if(o<10)then
-t=a(o+48)..t
-else
-t=a(o+87)..t
-end
-i=u(i/16)
+a=e(i,15)
+if(a<10)then
+t=o(a+48)..t
+else
+t=o(a+87)..t
+end
+i=h(i/16)
 end
 return t
 end
-local function b(t)
-local i,o
+local function k(t)
+local i,a
 local n=""
-i=l(t)*8
-t=t..a(128)
-o=56-e(l(t),63)
-if(o<0)then
-o=o+64
-end
-for e=1,o do
-t=t..a(0)
+i=r(t)*8
+t=t..o(128)
+a=56-e(r(t),63)
+if(a<0)then
+a=a+64
+end
+for e=1,a do
+t=t..o(0)
 end
 for t=1,8 do
-n=a(e(i,255))..n
-i=u(i/256)
+n=o(e(i,255))..n
+i=h(i/256)
 end
 return t..n
 end
-local function k(w)
-local u,t,a,o,m,l,c,v
-local i,i
-local i={}
-while(w~="")do
+local function b(f)
+local s,t,i,o,w,h,r,v
+local a,a
+local a={}
+while(f~="")do
 for e=0,15 do
-i[e]=0
+a[e]=0
 for t=1,4 do
-i[e]=i[e]*256+g(w,e*4+t)
+a[e]=a[e]*256+j(f,e*4+t)
 end
 end
 for e=16,79 do
-i[e]=p(n(n(i[e-3],i[e-8]),n(i[e-14],i[e-16])),1)
-end
-u=s
-t=h
-a=d
-o=r
-m=f
-for h=0,79 do
-if(h<20)then
-l=y(e(t,a),e(j(t),o))
-c=1518500249
-elseif(h<40)then
-l=n(n(t,a),o)
-c=1859775393
-elseif(h<60)then
-l=y(y(e(t,a),e(t,o)),e(a,o))
-c=2400959708
-else
-l=n(n(t,a),o)
-c=3395469782
-end
-v=p(u,5)+l+m+c+i[h]
-m=o
-o=a
-a=p(t,30)
-t=u
-u=v
-end
-s=e(s+u,4294967295)
-h=e(h+t,4294967295)
-d=e(d+a,4294967295)
-r=e(r+o,4294967295)
-f=e(f+m,4294967295)
-w=q(w,65)
-end
-end
-local function a(e,t)
-e=b(e)
-s=1732584193
-h=4023233417
-d=2562383102
-r=271733878
-f=3285377520
-k(e)
-local e=m(s)..m(h)..m(d)
-..m(r)..m(f);
+a[e]=p(n(n(a[e-3],a[e-8]),n(a[e-14],a[e-16])),1)
+end
+s=m
+t=d
+i=c
+o=l
+w=u
+for d=0,79 do
+if(d<20)then
+h=y(e(t,i),e(g(t),o))
+r=1518500249
+elseif(d<40)then
+h=n(n(t,i),o)
+r=1859775393
+elseif(d<60)then
+h=y(y(e(t,i),e(t,o)),e(i,o))
+r=2400959708
+else
+h=n(n(t,i),o)
+r=3395469782
+end
+v=p(s,5)+h+w+r+a[d]
+w=o
+o=i
+i=p(t,30)
+t=s
+s=v
+end
+m=e(m+s,4294967295)
+d=e(d+t,4294967295)
+c=e(c+i,4294967295)
+l=e(l+o,4294967295)
+u=e(u+w,4294967295)
+f=q(f,65)
+end
+end
+local function t(e,t)
+e=k(e)
+m=1732584193
+d=4023233417
+c=2562383102
+l=271733878
+u=3285377520
+b(e)
+local e=s(m)..s(d)..s(c)
+..s(l)..s(u);
 if t then
 return e;
 else
@@ -127,127 +174,125 @@
 end));
 end
 end
-_G.sha1={sha1=a};
+_G.sha1={sha1=t};
 return _G.sha1;
 end)
 package.preload['lib.adhoc']=(function(...)
-local n,h=require"util.stanza",require"util.uuid";
+local s,r=require"util.stanza",require"util.uuid";
 local e="http://jabber.org/protocol/commands";
 local i={}
-local s={};
-function _cmdtag(i,o,t,a)
-local e=n.stanza("command",{xmlns=e,node=i.node,status=o});
+local h={};
+local function n(o,i,t,a)
+local e=s.stanza("command",{xmlns=e,node=o.node,status=i});
 if t then e.attr.sessionid=t;end
 if a then e.attr.action=a;end
 return e;
 end
-function s.new(t,e,a,o)
-return{name=t,node=e,handler=a,cmdtag=_cmdtag,permission=(o or"user")};
-end
-function s.handle_cmd(a,s,o)
-local e=o.tags[1].attr.sessionid or h.generate();
-local t={};
-t.to=o.attr.to;
-t.from=o.attr.from;
-t.action=o.tags[1].attr.action or"execute";
-t.form=o.tags[1]:child_with_ns("jabber:x:data");
-local t,h=a:handler(t,i[e]);
-i[e]=h;
-local o=n.reply(o);
-if t.status=="completed"then
+function h.new(o,a,t,e)
+return{name=o,node=a,handler=t,cmdtag=n,permission=(e or"user")};
+end
+function h.handle_cmd(o,h,t)
+local e=t.tags[1].attr.sessionid or r.generate();
+local a={};
+a.to=t.attr.to;
+a.from=t.attr.from;
+a.action=t.tags[1].attr.action or"execute";
+a.form=t.tags[1]:child_with_ns("jabber:x:data");
+local a,n=o:handler(a,i[e]);
+i[e]=n;
+local n=s.reply(t);
+local t;
+if a.status=="completed"then
 i[e]=nil;
-cmdtag=a:cmdtag("completed",e);
-elseif t.status=="canceled"then
+t=o:cmdtag("completed",e);
+elseif a.status=="canceled"then
 i[e]=nil;
-cmdtag=a:cmdtag("canceled",e);
-elseif t.status=="error"then
+t=o:cmdtag("canceled",e);
+elseif a.status=="error"then
 i[e]=nil;
-o=n.error_reply(o,t.error.type,t.error.condition,t.error.message);
-s.send(o);
+n=s.error_reply(n,a.error.type,a.error.condition,a.error.message);
+h.send(n);
 return true;
 else
-cmdtag=a:cmdtag("executing",e);
-end
-for t,e in pairs(t)do
-if t=="info"then
-cmdtag:tag("note",{type="info"}):text(e):up();
-elseif t=="warn"then
-cmdtag:tag("note",{type="warn"}):text(e):up();
-elseif t=="error"then
-cmdtag:tag("note",{type="error"}):text(e.message):up();
-elseif t=="actions"then
-local t=n.stanza("actions");
-for o,e in ipairs(e)do
+t=o:cmdtag("executing",e);
+end
+for a,e in pairs(a)do
+if a=="info"then
+t:tag("note",{type="info"}):text(e):up();
+elseif a=="warn"then
+t:tag("note",{type="warn"}):text(e):up();
+elseif a=="error"then
+t:tag("note",{type="error"}):text(e.message):up();
+elseif a=="actions"then
+local a=s.stanza("actions");
+for i,e in ipairs(e)do
 if(e=="prev")or(e=="next")or(e=="complete")then
-t:tag(e):up();
-else
-module:log("error",'Command "'..a.name..
-'" at node "'..a.node..'" provided an invalid action "'..e..'"');
-end
-end
-cmdtag:add_child(t);
-elseif t=="form"then
-cmdtag:add_child((e.layout or e):form(e.values));
-elseif t=="result"then
-cmdtag:add_child((e.layout or e):form(e.values,"result"));
-elseif t=="other"then
-cmdtag:add_child(e);
-end
-end
-o:add_child(cmdtag);
-s.send(o);
+a:tag(e):up();
+else
+module:log("error",'Command "'..o.name..
+'" at node "'..o.node..'" provided an invalid action "'..e..'"');
+end
+end
+t:add_child(a);
+elseif a=="form"then
+t:add_child((e.layout or e):form(e.values));
+elseif a=="result"then
+t:add_child((e.layout or e):form(e.values,"result"));
+elseif a=="other"then
+t:add_child(e);
+end
+end
+n:add_child(t);
+h.send(n);
 return true;
 end
-return s;
+return h;
 end)
 package.preload['util.stanza']=(function(...)
 local t=table.insert;
-local e=table.concat;
-local r=table.remove;
-local w=table.concat;
-local s=string.format;
-local y=string.match;
-local c=tostring;
+local d=table.remove;
+local y=table.concat;
+local h=string.format;
+local l=string.match;
+local f=tostring;
 local m=setmetatable;
-local e=getmetatable;
-local i=pairs;
-local n=ipairs;
-local o=type;
-local e=next;
-local e=print;
-local e=unpack;
-local f=string.gsub;
-local e=string.char;
-local u=string.find;
+local p=getmetatable;
+local n=pairs;
+local o=ipairs;
+local i=type;
+local b=string.gsub;
+local w=string.sub;
+local c=string.find;
 local e=os;
-local l=not e.getenv("WINDIR");
-local d,a;
-if l then
+local u=not e.getenv("WINDIR");
+local r,a;
+if u then
 local t,e=pcall(require,"util.termcolours");
 if t then
-d,a=e.getstyle,e.getstring;
-else
-l=nil;
-end
-end
-local p="urn:ietf:params:xml:ns:xmpp-stanzas";
-module"stanza"
-stanza_mt={__type="stanza"};
-stanza_mt.__index=stanza_mt;
-local e=stanza_mt;
-function stanza(t,a)
+r,a=e.getstyle,e.getstring;
+else
+u=nil;
+end
+end
+local v="urn:ietf:params:xml:ns:xmpp-stanzas";
+local e=nil;
+local e={__type="stanza"};
+e.__index=e;
+local function s(t,a)
 local t={name=t,attr=a or{},tags={}};
 return m(t,e);
 end
-local h=stanza;
+local function k(t)
+return p(t)==e;
+end
 function e:query(e)
 return self:tag("query",{xmlns=e});
 end
 function e:body(t,e)
 return self:tag("body",e):text(t);
 end
-function e:tag(a,e)
-local a=h(a,e);
+function e:tag(e,a)
+local a=s(e,a);
 local e=self.last_add;
 if not e then e={};self.last_add=e;end
 (e[#e]or self):add_direct_child(a);
@@ -261,7 +306,7 @@
 end
 function e:up()
 local e=self.last_add;
-if e then r(e);end
+if e then d(e);end
 return self;
 end
 function e:reset()
@@ -269,7 +314,7 @@
 return self;
 end
 function e:add_direct_child(e)
-if o(e)=="table"then
+if i(e)=="table"then
 t(self.tags,e);
 end
 t(self,e);
@@ -279,11 +324,11 @@
 (e and e[#e]or self):add_direct_child(t);
 return self;
 end
-function e:get_child(a,t)
-for o,e in n(self.tags)do
-if(not a or e.name==a)
-and((not t and self.attr.xmlns==e.attr.xmlns)
-or e.attr.xmlns==t)then
+function e:get_child(t,a)
+for o,e in o(self.tags)do
+if(not t or e.name==t)
+and((not a and self.attr.xmlns==e.attr.xmlns)
+or e.attr.xmlns==a)then
 return e;
 end
 end
@@ -296,12 +341,12 @@
 return nil;
 end
 function e:child_with_name(t)
-for a,e in n(self.tags)do
+for a,e in o(self.tags)do
 if e.name==t then return e;end
 end
 end
 function e:child_with_ns(t)
-for a,e in n(self.tags)do
+for a,e in o(self.tags)do
 if e.attr.xmlns==t then return e;end
 end
 end
@@ -312,60 +357,80 @@
 return t[e];
 end,self,e;
 end
-function e:childtags(i,e)
-e=e or self.attr.xmlns;
-local t=self.tags;
-local a,o=1,#t;
+function e:childtags(o,i)
+local e=self.tags;
+local a,t=1,#e;
 return function()
-for o=a,o do
-local t=t[o];
-if(not i or t.name==i)
-and(not e or e==t.attr.xmlns)then
-a=o+1;
-return t;
+for t=a,t do
+local e=e[t];
+if(not o or e.name==o)
+and((not i and self.attr.xmlns==e.attr.xmlns)
+or e.attr.xmlns==i)then
+a=t+1;
+return e;
 end
 end
 end;
 end
 function e:maptags(i)
-local a,t=self.tags,1;
-local n,o=#self,#a;
-local e=1;
-while t<=o do
-if self[e]==a[t]then
-local i=i(self[e]);
+local o,e=self.tags,1;
+local n,a=#self,#o;
+local t=1;
+while e<=a and a>0 do
+if self[t]==o[e]then
+local i=i(self[t]);
 if i==nil then
-r(self,e);
-r(a,t);
+d(self,t);
+d(o,e);
 n=n-1;
-o=o-1;
-else
-self[e]=i;
-a[e]=i;
+a=a-1;
+t=t-1;
+e=e-1;
+else
+self[t]=i;
+o[e]=i;
 end
 e=e+1;
+end
 t=t+1;
 end
-end
 return self;
 end
-local r
-do
-local e={["'"]="&apos;",["\""]="&quot;",["<"]="&lt;",[">"]="&gt;",["&"]="&amp;"};
-function r(t)return(f(t,"['&<>\"]",e));end
-_M.xml_escape=r;
-end
-local function f(o,e,h,a,r)
-local n=0;
+function e:find(a)
+local e=1;
+local s=#a+1;
+repeat
+local o,t,n;
+local i=w(a,e,e);
+if i=="@"then
+return self.attr[w(a,e+1)];
+elseif i=="{"then
+o,e=l(a,"^([^}]+)}()",e+1);
+end
+t,n,e=l(a,"^([^@/#]*)([/#]?)()",e);
+t=t~=""and t or nil;
+if e==s then
+if n=="#"then
+return self:get_child_text(t,o);
+end
+return self:get_child(t,o);
+end
+self=self:get_child(t,o);
+until not self
+end
+local w={["'"]="&apos;",["\""]="&quot;",["<"]="&lt;",[">"]="&gt;",["&"]="&amp;"};
+local function d(e)return(b(e,"['&<>\"]",w));end
+local function w(o,e,h,a,r)
+local i=0;
 local s=o.name
 t(e,"<"..s);
-for o,i in i(o.attr)do
-if u(o,"\1",1,true)then
-local o,s=y(o,"^([^\1]*)\1?(.*)$");
-n=n+1;
-t(e," xmlns:ns"..n.."='"..a(o).."' ".."ns"..n..":"..s.."='"..a(i).."'");
-elseif not(o=="xmlns"and i==r)then
-t(e," "..o.."='"..a(i).."'");
+for o,n in n(o.attr)do
+if c(o,"\1",1,true)then
+local o,s=l(o,"^([^\1]*)\1?(.*)$");
+i=i+1;
+t(e," xmlns:ns"..i.."='"..a(o).."' ".."ns"..i..":"..s.."='"..a(n).."'");
+elseif not(o=="xmlns"and n==r)then
+t(e," "..o.."='"..a(n).."'");
 end
 end
 local i=#o;
@@ -386,89 +451,84 @@
 end
 function e.__tostring(t)
 local e={};
-f(t,e,f,r,nil);
-return w(e);
-end
-function e.top_tag(t)
-local e="";
-if t.attr then
-for t,a in i(t.attr)do if o(t)=="string"then e=e..s(" %s='%s'",t,r(c(a)));end end
-end
-return s("<%s%s>",t.name,e);
+w(t,e,w,d,nil);
+return y(e);
+end
+function e.top_tag(e)
+local t="";
+if e.attr then
+for e,a in n(e.attr)do if i(e)=="string"then t=t..h(" %s='%s'",e,d(f(a)));end end
+end
+return h("<%s%s>",e.name,t);
 end
 function e.get_text(e)
 if#e.tags==0 then
-return w(e);
-end
-end
-function e.get_error(a)
-local o,e,t;
-local a=a:get_child("error");
-if not a then
+return y(e);
+end
+end
+function e.get_error(e)
+local i,t,a;
+local e=e:get_child("error");
+if not e then
 return nil,nil,nil;
 end
-o=a.attr.type;
-for a in a:childtags()do
-if a.attr.xmlns==p then
-if not t and a.name=="text"then
-t=a:get_text();
-elseif not e then
-e=a.name;
-end
-if e and t then
+i=e.attr.type;
+for o,e in o(e.tags)do
+if e.attr.xmlns==v then
+if not a and e.name=="text"then
+a=e:get_text();
+elseif not t then
+t=e.name;
+end
+if t and a then
 break;
 end
 end
 end
-return o,e or"undefined-condition",t;
-end
-function e.__add(t,e)
-return t:add_direct_child(e);
-end
-do
-local e=0;
-function new_id()
-e=e+1;
-return"lx"..e;
-end
-end
-function preserialize(a)
-local e={name=a.name,attr=a.attr};
-for i,a in n(a)do
-if o(a)=="table"then
-t(e,preserialize(a));
-else
-t(e,a);
-end
-end
-return e;
-end
-function deserialize(a)
+return i,t or"undefined-condition",a;
+end
+local w=0;
+local function y()
+w=w+1;
+return"lx"..w;
+end
+local function w(e)
+local a={name=e.name,attr=e.attr};
+for o,e in o(e)do
+if i(e)=="table"then
+t(a,w(e));
+else
+t(a,e);
+end
+end
+return a;
+end
+local function p(a)
 if a then
 local s=a.attr;
 for e=1,#s do s[e]=nil;end
 local h={};
-for e in i(s)do
-if u(e,"|",1,true)and not u(e,"\1",1,true)then
-local t,a=y(e,"^([^|]+)|(.+)$");
-h[t.."\1"..a]=s[e];
+for e in n(s)do
+if c(e,"|",1,true)and not c(e,"\1",1,true)then
+local a,t=l(e,"^([^|]+)|(.+)$");
+h[a.."\1"..t]=s[e];
 s[e]=nil;
 end
 end
-for t,e in i(h)do
-s[t]=e;
+for e,t in n(h)do
+s[e]=t;
 end
 m(a,e);
-for t,e in n(a)do
-if o(e)=="table"then
-deserialize(e);
+for t,e in o(a)do
+if i(e)=="table"then
+p(e);
 end
 end
 if not a.tags then
 local e={};
-for n,i in n(a)do
-if o(i)=="table"then
-t(e,i);
+for n,o in o(a)do
+if i(o)=="table"then
+t(e,o);
 end
 end
 a.tags=e;
@@ -476,263 +536,323 @@
 end
 return a;
 end
-local function u(a)
-local o,n={},{};
-for t,e in i(a.attr)do o[t]=e;end
-local o={name=a.name,attr=o,tags=n};
+local function l(a)
+local o,i={},{};
+for t,e in n(a.attr)do o[t]=e;end
+local o={name=a.name,attr=o,tags=i};
 for e=1,#a do
 local e=a[e];
 if e.name then
-e=u(e);
-t(n,e);
+e=l(e);
+t(i,e);
 end
 t(o,e);
 end
 return m(o,e);
 end
-clone=u;
-function message(t,e)
+local function b(t,e)
 if not e then
-return h("message",t);
-else
-return h("message",t):tag("body"):text(e):up();
-end
-end
-function iq(e)
-if e and not e.id then e.id=new_id();end
-return h("iq",e or{id=new_id()});
-end
-function reply(e)
-return h(e.name,e.attr and{to=e.attr.from,from=e.attr.to,id=e.attr.id,type=((e.name=="iq"and"result")or e.attr.type)});
-end
-do
-local t={xmlns=p};
-function error_reply(e,i,o,a)
-local e=reply(e);
+return s("message",t);
+else
+return s("message",t):tag("body"):text(e):up();
+end
+end
+local function g(e)
+if e and not e.id then e.id=y();end
+return s("iq",e or{id=y()});
+end
+local function m(e)
+return s(e.name,e.attr and{to=e.attr.from,from=e.attr.to,id=e.attr.id,type=((e.name=="iq"and"result")or e.attr.type)});
+end
+local c={xmlns=v};
+local function v(e,o,a,t)
+local e=m(e);
 e.attr.type="error";
-e:tag("error",{type=i})
-:tag(o,t):up();
-if(a)then e:tag("text",t):text(a):up();end
+e:tag("error",{type=o})
+:tag(a,c):up();
+if t then e:tag("text",c):text(t):up();end
 return e;
 end
-end
-function presence(e)
-return h("presence",e);
-end
-if l then
-local u=d("yellow");
-local l=d("red");
-local h=d("red");
-local t=d("magenta");
-local d=" "..a(u,"%s")..a(t,"=")..a(l,"'%s'");
-local l=a(t,"<")..a(h,"%s").."%s"..a(t,">");
-local h=l.."%s"..a(t,"</")..a(h,"%s")..a(t,">");
-function e.pretty_print(e)
-local t="";
-for a,e in n(e)do
-if o(e)=="string"then
-t=t..r(e);
-else
-t=t..e:pretty_print();
+local function c(e)
+return s("presence",e);
+end
+if u then
+local s=r("yellow");
+local u=r("red");
+local l=r("red");
+local t=r("magenta");
+local r=" "..a(s,"%s")..a(t,"=")..a(u,"'%s'");
+local s=a(t,"<")..a(l,"%s").."%s"..a(t,">");
+local l=s.."%s"..a(t,"</")..a(l,"%s")..a(t,">");
+function e.pretty_print(t)
+local e="";
+for a,t in o(t)do
+if i(t)=="string"then
+e=e..d(t);
+else
+e=e..t:pretty_print();
 end
 end
 local a="";
-if e.attr then
-for e,t in i(e.attr)do if o(e)=="string"then a=a..s(d,e,c(t));end end
-end
-return s(h,e.name,a,t,e.name);
-end
-function e.pretty_top_tag(e)
-local t="";
-if e.attr then
-for e,a in i(e.attr)do if o(e)=="string"then t=t..s(d,e,c(a));end end
-end
-return s(l,e.name,t);
+if t.attr then
+for e,t in n(t.attr)do if i(e)=="string"then a=a..h(r,e,f(t));end end
+end
+return h(l,t.name,a,e,t.name);
+end
+function e.pretty_top_tag(t)
+local e="";
+if t.attr then
+for t,a in n(t.attr)do if i(t)=="string"then e=e..h(r,t,f(a));end end
+end
+return h(s,t.name,e);
 end
 else
 e.pretty_print=e.__tostring;
 e.pretty_top_tag=e.top_tag;
 end
-return _M;
+return{
+stanza_mt=e;
+stanza=s;
+is_stanza=k;
+new_id=y;
+preserialize=w;
+deserialize=p;
+clone=l;
+message=b;
+iq=g;
+reply=m;
+error_reply=v;
+presence=c;
+xml_escape=d;
+};
 end)
 package.preload['util.timer']=(function(...)
-local c=require"net.server".addtimer;
-local o=require"net.server".event;
-local l=require"net.server".event_base;
-local r=math.min
-local u=math.huge
-local d=require"socket".gettime;
-local s=table.insert;
-local e=table.remove;
-local e,n=ipairs,pairs;
-local h=type;
+local a=require"net.server";
+local s=math.min
+local l=math.huge
+local n=require"util.time".now
+local h=table.insert;
+local r=pairs;
+local d=type;
 local i={};
 local e={};
-module"timer"
+local t=nil;
 local t;
-if not o then
-function t(a,o)
-local i=d();
-a=a+i;
-if a>=i then
-s(e,{a,o});
-else
-local e=o();
-if e and h(e)=="number"then
-return t(e,o);
-end
-end
-end
-c(function()
-local o=d();
+if not a.event then
+function t(o,i)
+local n=n();
+o=o+n;
+if o>=n then
+h(e,{o,i});
+else
+local e=i(n);
+if e and d(e)=="number"then
+return t(e,i);
+end
+end
+end
+a._addtimer(function()
+local a=n();
 if#e>0 then
-for a,t in n(e)do
-s(i,t);
+for a,t in r(e)do
+h(i,t);
 end
 e={};
 end
-local e=u;
-for d,a in n(i)do
-local n,s=a[1],a[2];
-if n<=o then
-i[d]=nil;
-local a=s(o);
-if h(a)=="number"then
-t(a,s);
-e=r(e,a);
-end
-else
-e=r(e,n-o);
+local e=l;
+for h,o in r(i)do
+local o,n=o[1],o[2];
+if o<=a then
+i[h]=nil;
+local a=n(a);
+if d(a)=="number"then
+t(a,n);
+e=s(e,a);
+end
+else
+e=s(e,o-a);
 end
 end
 return e;
 end);
 else
-local o=(o.core and o.core.LEAVE)or-1;
-function t(a,t)
-local e;
-e=l:addevent(nil,0,function()
-local t=t();
-if t then
-return 0,t;
-elseif e then
-return o;
+local e=a.event;
+local o=a.event_base;
+local i=(e.core and e.core.LEAVE)or-1;
+function t(a,e)
+local t;
+t=o:addevent(nil,0,function()
+local e=e(n());
+if e then
+return 0,e;
+elseif t then
+return i;
 end
 end
 ,a);
 end
 end
+return{
 add_task=t;
-return _M;
+};
 end)
 package.preload['util.termcolours']=(function(...)
-local a,n=table.concat,table.insert;
-local t,i=string.char,string.format;
-local h=ipairs;
-local s=io.write;
-local e;
+local h,s=table.concat,table.insert;
+local a,r=string.char,string.format;
+local o=tonumber;
+local d=ipairs;
+local l=io.write;
+local i=math.floor;
+local u=type;
+local m=setmetatable;
+local f=pairs;
+local t;
 if os.getenv("WINDIR")then
-e=require"util.windows";
-end
-local o=e and e.get_consolecolor and e.get_consolecolor();
-module"termcolours"
-local r={
+t=require"util.windows";
+end
+local n=t and t.get_consolecolor and t.get_consolecolor();
+local e=nil;
+local e={
 reset=0;bright=1,dim=2,underscore=4,blink=5,reverse=7,hidden=8;
 black=30;red=31;green=32;yellow=33;blue=34;magenta=35;cyan=36;white=37;
 ["black background"]=40;["red background"]=41;["green background"]=42;["yellow background"]=43;["blue background"]=44;["magenta background"]=45;["cyan background"]=46;["white background"]=47;
 bold=1,dark=2,underline=4,underlined=4,normal=0;
 }
-local d={
-["0"]=o,
+local c={
+["0"]=n,
 ["1"]=7+8,
 ["1;33"]=2+4+8,
 ["1;31"]=4+8
 }
-local l=t(27).."[%sm%s"..t(27).."[0m";
-function getstring(e,t)
+local p={
+[1]="font-weight: bold",[2]="opacity: 0.5",[4]="text-decoration: underline",[8]="visibility: hidden",
+[30]="color:black",[31]="color:red",[32]="color:green",[33]="color:#FFD700",
+[34]="color:blue",[35]="color: magenta",[36]="color:cyan",[37]="color: white",
+[40]="background-color:black",[41]="background-color:red",[42]="background-color:green",
+[43]="background-color:yellow",[44]="background-color:blue",[45]="background-color: magenta",
+[46]="background-color:cyan",[47]="background-color: white";
+};
+local a=a(27).."[%sm%s"..a(27).."[0m";
+local function y(e,t)
 if e then
-return i(l,e,t);
+return r(a,e,t);
 else
 return t;
 end
 end
-function getstyle(...)
-local e,t={...},{};
-for a,e in h(e)do
-e=r[e];
-if e then
-n(t,e);
-end
-end
-return a(t,";");
+local function w(e)
+return i(e*3/32)+232;
+end
+local function r(e,t,a)
+if e==t and t==a then
+return w(e);
+end
+e=i(e*3/128);
+t=i(t*3/128);
+a=i(a*3/128);
+return 16+(e*36)+(t*6)+(a);
+end
+local function a(e)
+local t=o(e:sub(1,2),16);
+local a=o(e:sub(3,4),16);
+local e=o(e:sub(5,6),16);
+return t,a,e;
+end
+m(e,{__index=function(t,e)
+if u(e)=="string"and e:find("%x%x%x%x%x%x")==1 then
+local t=e:sub(7)==" background"and"48;5;"or"38;5;";
+return t..r(a(e));
+end
+end});
+local a={
+red="ff0000";fuchsia="ff00ff";green="008000";white="ffffff";
+lime="00ff00";yellow="ffff00";purple="800080";blue="0000ff";
+aqua="00ffff";olive="808000";black="000000";navy="000080";
+teal="008080";silver="c0c0c0";maroon="800000";gray="808080";
+}
+for t,a in f(a)do
+e[t]=e[t]or e[a];
+t,a=t.." background",a.." background"
+e[t]=e[t]or e[a];
+end
+local function r(...)
+local t,a={...},{};
+for o,t in d(t)do
+t=e[t];
+if t then
+s(a,t);
+end
+end
+return h(a,";");
 end
 local a="0";
-function setstyle(e)
+local function i(e)
+e=e or"0";
+if e~=a then
+l("\27["..e.."m");
+a=e;
+end
+end
+if t then
+function i(e)
 e=e or"0";
 if e~=a then
-s("\27["..e.."m");
+t.set_consolecolor(c[e]or n);
 a=e;
 end
 end
-if e then
-function setstyle(t)
-t=t or"0";
-if t~=a then
-e.set_consolecolor(d[t]or o);
-a=t;
-end
-end
-if not o then
-function setstyle(e)end
-end
-end
-return _M;
+if not n then
+function i()end
+end
+end
+local function a(t)
+if t=="0"then return"</span>";end
+local e={};
+for t in t:gmatch("[^;]+")do
+s(e,p[o(t)]);
+end
+return"</span><span style='"..h(e,";").."'>";
+end
+local function e(e)
+return e:gsub("\027%[(.-)m",a);
+end
+return{
+getstring=y;
+getstyle=r;
+setstyle=i;
+tohtml=e;
+};
 end)
 package.preload['util.uuid']=(function(...)
-local e=math.random;
-local i=tostring;
-local e=os.time;
-local a=os.clock;
-local n=require"util.hashes".sha1;
-module"uuid"
-local t=0;
+local a=require"util.random";
+local t=a.bytes;
+local o=require"util.hex".to;
+local i=math.ceil;
+local function e(e)
+return o(t(i(e/2))):sub(1,e);
+end
 local function o()
-local e=e();
-if t>=e then e=t+1;end
-t=e;
-return e;
-end
-local function e(e)
-return n(e..a()..i({}),true);
-end
-local t=e(o());
-local function a(a)
-t=e(t..a);
-end
-local function e(e)
-if#t<e then a(o());end
-local a=t:sub(0,e);
-t=t:sub(e+1);
-return a;
+return("%x"):format(t(1):byte()%4+8);
 end
 local function t()
-return("%x"):format(e(1):byte()%4+8);
-end
-function generate()
-return e(8).."-"..e(4).."-4"..e(3).."-"..(t())..e(3).."-"..e(12);
-end
-seed=a;
-return _M;
+return e(8).."-"..e(4).."-4"..e(3).."-"..(o())..e(3).."-"..e(12);
+end
+return{
+get_nibbles=e;
+generate=t;
+seed=a.seed;
+};
 end)
 package.preload['net.dns']=(function(...)
-local s=require"socket";
-local q=require"util.timer";
-local e,b=pcall(require,"util.windows");
-local E=(e and b)or os.getenv("WINDIR");
-local c,z,y,a,n=
+local i=require"socket";
+local k=require"util.timer";
+local z=require"util.ip".new_ip;
+local e,w=pcall(require,"util.windows");
+local E=(e and w)or os.getenv("WINDIR");
+local l,_,v,a,n=
 coroutine,io,math,string,table;
-local p,h,o,m,r,v,x,k,t,e,j=
-ipairs,next,pairs,print,setmetatable,tostring,assert,error,unpack,select,type;
+local c,s,o,m,u,b,x,q,e,T=
+ipairs,next,pairs,print,setmetatable,tostring,assert,error,select,type;
 local e={
 get=function(t,...)
 local a=e('#',...);
@@ -744,7 +864,7 @@
 end;
 set=function(a,...)
 local i=e('#',...);
-local s,o=e(i-1,...);
+local h,o=e(i-1,...);
 local t,n;
 for i=1,i-2 do
 local i=e(i,...)
@@ -752,7 +872,7 @@
 if o==nil then
 if e==nil then
 return;
-elseif h(e,h(e))then
+elseif s(e,s(e))then
 t=nil;n=nil;
 elseif t==nil then
 t=a;n=i;
@@ -766,20 +886,20 @@
 if o==nil and t then
 t[n]=nil;
 else
-a[s]=o;
+a[h]=o;
 return o;
 end
 end;
 };
-local d,l=e.get,e.set;
-local _=15;
-module('dns')
-local t=_M;
-local i=n.insert
-local function u(e)
+local r,d=e.get,e.set;
+local j=15;
+local e=nil;
+local t={};
+local h=n.insert
+local function f(e)
 return(e-(e%256))/256;
 end
-local function w(e)
+local function y(e)
 local t={};
 for o,e in o(e)do
 t[o]=e;
@@ -788,10 +908,10 @@
 end
 return t;
 end
-local function f(i)
+local function p(i)
 local e={};
 for o,i in o(i)do
-local t=a.char(u(o),o%256);
+local t=a.char(f(o),o%256);
 e[o]=t;
 e[i]=t;
 e[a.lower(i)]=t;
@@ -804,41 +924,43 @@
 [28]='AAAA',[29]='LOC',[33]='SRV',
 [252]='AXFR',[253]='MAILB',[254]='MAILA',[255]='*'};
 t.classes={'IN','CS','CH','HS',[255]='*'};
-t.type=w(t.types);
-t.class=w(t.classes);
-t.typecode=f(t.types);
-t.classcode=f(t.classes);
-local function g(e,i,o)
+t.type=y(t.types);
+t.class=y(t.classes);
+t.typecode=p(t.types);
+t.classcode=p(t.classes);
+local function g(e,o,i)
 if a.byte(e,-1)~=46 then e=e..'.';end
 e=a.lower(e);
-return e,t.type[i or'A'],t.class[o or'IN'];
-end
-local function w(t,a,i)
-a=a or s.gettime();
-for o,e in o(t)do
+return e,t.type[o or'A'],t.class[i or'IN'];
+end
+local function p(t,a,s)
+a=a or i.gettime();
+for o,e in c(t)do
 if e.tod then
-e.ttl=y.floor(e.tod-a);
+e.ttl=v.floor(e.tod-a);
 if e.ttl<=0 then
+t[e[e.type:lower()]]=nil;
 n.remove(t,o);
-return w(t,a,i);
-end
-elseif i=='soft'then
+return p(t,a,s);
+end
+elseif s=='soft'then
 x(e.ttl==0);
-t[o]=nil;
+t[e[e.type:lower()]]=nil;
+n.remove(t,o);
 end
 end
 end
 local e={};
 e.__index=e;
-e.timeout=_;
-local function x(e)
+e.timeout=j;
+local function j(e)
 local e=e.type and e[e.type:lower()];
-if j(e)~="string"then
+if T(e)~="string"then
 return"<UNKNOWN RDATA TYPE>";
 end
 return e;
 end
-local f={
+local y={
 LOC=e.LOC_tostring;
 MX=function(e)
 return a.format('%2i %s',e.pref,e.mx);
@@ -848,46 +970,39 @@
 return a.format('%5d %5d %5d %s',e.priority,e.weight,e.port,e.target);
 end;
 };
-local j={};
-function j.__tostring(e)
-local t=(f[e.type]or x)(e);
+local x={};
+function x.__tostring(e)
+local t=(y[e.type]or j)(e);
 return a.format('%2s %-5s %6i %-28s %s',e.class,e.type,e.ttl,e.name,t);
 end
-local x={};
-function x.__tostring(t)
+local j={};
+function j.__tostring(t)
 local e={};
-for a,t in o(t)do
-i(e,v(t)..'\n');
+for a,t in c(t)do
+h(e,b(t)..'\n');
 end
 return n.concat(e);
 end
-local f={};
-function f.__tostring(t)
-local a=s.gettime();
-local e={};
-for n,t in o(t)do
-for n,t in o(t)do
-for o,t in o(t)do
-w(t,a);
-i(e,v(t));
-end
-end
-end
-return n.concat(e);
-end
-function e:new()
-local t={active={},cache={},unsorted={}};
-r(t,e);
-r(t.cache,f);
-r(t.unsorted,{__mode='kv'});
-return t;
+local y={};
+function y.__tostring(e)
+local a=i.gettime();
+local t={};
+for i,e in o(e)do
+for i,e in o(e)do
+for o,e in o(e)do
+p(e,a);
+h(t,b(e));
+end
+end
+end
+return n.concat(t);
 end
 function t.random(...)
-y.randomseed(y.floor(1e4*s.gettime()));
-t.random=y.random;
+v.randomseed(v.floor(1e4*i.gettime())%2147483648);
+t.random=v.random;
 return t.random(...);
 end
-local function y(e)
+local function v(e)
 e=e or{};
 e.id=e.id or t.random(0,65535);
 e.rd=e.rd or 1;
@@ -903,48 +1018,48 @@
 e.nscount=e.nscount or 0;
 e.arcount=e.arcount or 0;
 local t=a.char(
-u(e.id),e.id%256,
+f(e.id),e.id%256,
 e.rd+2*e.tc+4*e.aa+8*e.opcode+128*e.qr,
 e.rcode+16*e.z+128*e.ra,
-u(e.qdcount),e.qdcount%256,
-u(e.ancount),e.ancount%256,
-u(e.nscount),e.nscount%256,
-u(e.arcount),e.arcount%256
+f(e.qdcount),e.qdcount%256,
+f(e.ancount),e.ancount%256,
+f(e.nscount),e.nscount%256,
+f(e.arcount),e.arcount%256
 );
 return t,e.id;
 end
-local function u(t)
+local function f(t)
 local e={};
 for t in a.gmatch(t,'[^.]+')do
-i(e,a.char(a.len(t)));
-i(e,t);
-end
-i(e,a.char(0));
+h(e,a.char(a.len(t)));
+h(e,t);
+end
+h(e,a.char(0));
 return n.concat(e);
 end
-local function _(a,e,o)
-a=u(a);
-e=t.typecode[e or'a'];
-o=t.classcode[o or'in'];
-return a..e..o;
+local function T(o,a,e)
+o=f(o);
+a=t.typecode[a or'a'];
+e=t.classcode[e or'in'];
+return o..a..e;
 end
 function e:byte(e)
 e=e or 1;
 local t=self.offset;
 local o=t+e-1;
 if o>#self.packet then
-k(a.format('out of bounds: %i>%i',o,#self.packet));
+q(a.format('out of bounds: %i>%i',o,#self.packet));
 end
 self.offset=t+e;
 return a.byte(self.packet,t,o);
 end
 function e:word()
-local e,t=self:byte(2);
-return 256*e+t;
+local t,e=self:byte(2);
+return 256*t+e;
 end
 function e:dword()
-local a,o,t,e=self:byte(4);
-return 16777216*a+65536*o+256*t+e;
+local e,t,o,a=self:byte(4);
+return 16777216*e+65536*t+256*o+a;
 end
 function e:sub(e)
 e=e or 1;
@@ -976,15 +1091,16 @@
 local t,a=nil,0;
 local e=self:byte();
 local o={};
+if e==0 then return"."end
 while e>0 do
 if e>=192 then
 a=a+1;
-if a>=20 then k('dns error: 20 pointers');end;
+if a>=20 then q('dns error: 20 pointers');end;
 local e=((e-192)*256)+self:byte();
 t=t or self.offset;
 self.offset=e+1;
 else
-i(o,self:sub(e)..'.');
+h(o,self:sub(e)..'.');
 end
 e=self:byte();
 end
@@ -998,15 +1114,15 @@
 e.class=t.class[self:word()];
 return e;
 end
-function e:A(i)
-local n,t,o,e=self:byte(4);
-i.a=a.format('%i.%i.%i.%i',n,t,o,e);
+function e:A(n)
+local i,o,e,t=self:byte(4);
+n.a=a.format('%i.%i.%i.%i',i,o,e,t);
 end
 function e:AAAA(a)
 local e={};
 for t=1,a.rdlength,2 do
-local a,t=self:byte(2);
-n.insert(e,("%02x%02x"):format(a,t));
+local t,a=self:byte(2);
+n.insert(e,("%02x%02x"):format(t,a));
 end
 e=n.concat(e,":"):gsub("%f[%x]0+(%x)","%1");
 local t={};
@@ -1044,7 +1160,7 @@
 e.loc.altitude=self:dword();
 end
 end
-local function u(e,i,t)
+local function f(e,i,t)
 e=e-2147483648;
 if e<0 then i=t;e=-e;end
 local n,t,o;
@@ -1056,10 +1172,10 @@
 end
 function e.LOC_tostring(e)
 local t={};
-i(t,a.format(
+h(t,a.format(
 '%s    %s    %.2fm %.2fm %.2fm %.2fm',
-u(e.loc.latitude,'N','S'),
-u(e.loc.longitude,'E','W'),
+f(e.loc.latitude,'N','S'),
+f(e.loc.longitude,'E','W'),
 (e.loc.altitude-1e7)/100,
 e.loc.size/100,
 e.loc.horiz_pre/100,
@@ -1087,7 +1203,7 @@
 end
 function e:rr()
 local e={};
-r(e,j);
+u(e,x);
 e.name=self:name(self);
 e.type=t.type[self:word()]or e.type;
 e.class=t.class[self:word()]or e.class;
@@ -1107,7 +1223,7 @@
 end
 function e:rrs(t)
 local e={};
-for t=1,t do i(e,self:rr());end
+for t=1,t do h(e,self:rr());end
 return e;
 end
 function e:decode(t,o)
@@ -1116,13 +1232,14 @@
 if not t then return nil;end
 local t={header=t};
 t.question={};
-local n=self.offset;
+local i=self.offset;
 for e=1,t.header.qdcount do
-i(t.question,self:question());
-end
-t.question.raw=a.sub(self.packet,n,self.offset-1);
+h(t.question,self:question());
+end
+t.question.raw=a.sub(self.packet,i,self.offset-1);
 if not o then
 if not self.active[t.header.id]or not self.active[t.header.id][t.question.raw]then
+self.active[t.header.id]=nil;
 return nil;
 end
 end
@@ -1134,7 +1251,7 @@
 e.delays={1,3};
 function e:addnameserver(e)
 self.server=self.server or{};
-i(self.server,e);
+h(self.server,e);
 end
 function e:setnameserver(e)
 self.server={};
@@ -1142,8 +1259,8 @@
 end
 function e:adddefaultnameservers()
 if E then
-if b and b.get_nameservers then
-for t,e in p(b.get_nameservers())do
+if w and w.get_nameservers then
+for t,e in c(w.get_nameservers())do
 self:addnameserver(e);
 end
 end
@@ -1152,15 +1269,16 @@
 self:addnameserver("208.67.220.220");
 end
 else
-local e=z.open("/etc/resolv.conf");
+local e=_.open("/etc/resolv.conf");
 if e then
 for e in e:lines()do
 e=e:gsub("#.*$","")
-:match('^%s*nameserver%s+(.*)%s*$');
+:match('^%s*nameserver%s+([%x:%.]*%%?%S*)%s*$');
 if e then
-e:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]",function(e)
-self:addnameserver(e)
-end);
+local e=z(e);
+if e then
+self:addnameserver(e.addr);
+end
 end
 end
 end
@@ -1169,22 +1287,29 @@
 end
 end
 end
-function e:getsocket(t)
+function e:getsocket(a)
 self.socket=self.socket or{};
 self.socketset=self.socketset or{};
-local e=self.socket[t];
+local e=self.socket[a];
 if e then return e;end
-local a;
-e,a=s.udp();
+local o,t;
+local n=self.server[a];
+if n:find(":")then
+e,t=i.udp6();
+else
+e,t=(i.udp4 or i.udp)();
+end
+if e and self.socket_wrapper then e,t=self.socket_wrapper(e,self);end
 if not e then
-return nil,a;
-end
-if self.socket_wrapper then e=self.socket_wrapper(e,self);end
+return nil,t;
+end
 e:settimeout(0);
-e:setsockname('*',0);
-e:setpeername(self.server[t],53);
-self.socket[t]=e;
-self.socketset[e]=t;
+self.socket[a]=e;
+self.socketset[e]=a;
+o,t=e:setsockname('*',0);
+if not o then return self:servfail(e,t);end
+o,t=e:setpeername(n,53);
+if not o then return self:servfail(e,t);end
 return e;
 end
 function e:voidsocket(e)
@@ -1195,86 +1320,100 @@
 self.socket[self.socketset[e]]=nil;
 self.socketset[e]=nil;
 end
+e:close();
 end
 function e:socket_wrapper_set(e)
 self.socket_wrapper=e;
 end
 function e:closeall()
-for t,e in p(self.socket)do
+for t,e in c(self.socket)do
 self.socket[t]=nil;
 self.socketset[e]=nil;
 e:close();
 end
 end
 function e:remember(e,t)
-local o,n,a=g(e.name,e.type,e.class);
+local i,o,a=g(e.name,e.type,e.class);
 if t~='*'then
-t=n;
-local t=d(self.cache,a,'*',o);
-if t then i(t,e);end
-end
-self.cache=self.cache or r({},f);
-local a=d(self.cache,a,t,o)or
-l(self.cache,a,t,o,r({},x));
-i(a,e);
+t=o;
+local t=r(self.cache,a,'*',i);
+if t then h(t,e);end
+end
+self.cache=self.cache or u({},y);
+local a=r(self.cache,a,t,i)or
+d(self.cache,a,t,i,u({},j));
+if not a[e[o:lower()]]then
+a[e[o:lower()]]=true;
+h(a,e);
+end
 if t=='MX'then self.unsorted[a]=true;end
 end
-local function i(e,t)
-return(e.pref==t.pref)and(e.mx<t.mx)or(e.pref<t.pref);
-end
-function e:peek(a,t,o)
-a,t,o=g(a,t,o);
-local e=d(self.cache,o,t,a);
-if not e then return nil;end
-if w(e,s.gettime())and t=='*'or not h(e)then
-l(self.cache,o,t,a,nil);
+local function f(t,e)
+return(t.pref==e.pref)and(t.mx<e.mx)or(t.pref<e.pref);
+end
+function e:peek(o,a,t,h)
+o,a,t=g(o,a,t);
+local e=r(self.cache,t,a,o);
+if not e then
+if h then if h<=0 then return end else h=3 end
+e=r(self.cache,t,"CNAME",o);
+if not(e and e[1])then return end
+return self:peek(e[1].cname,a,t,h-1);
+end
+if p(e,i.gettime())and a=='*'or not s(e)then
+d(self.cache,t,a,o,nil);
 return nil;
 end
-if self.unsorted[e]then n.sort(e,i);end
+if self.unsorted[e]then n.sort(e,f);self.unsorted[e]=nil;end
 return e;
 end
 function e:purge(e)
 if e=='soft'then
-self.time=s.gettime();
+self.time=i.gettime();
 for t,e in o(self.cache or{})do
 for t,e in o(e)do
 for t,e in o(e)do
-w(e,self.time,'soft')
-end
-end
-end
-else self.cache=r({},f);end
-end
-function e:query(a,e,t)
-a,e,t=g(a,e,t)
+p(e,self.time,'soft')
+end
+end
+end
+else self.cache=u({},y);end
+end
+function e:query(e,a,t)
+e,a,t=g(e,a,t)
+local n=l.running();
+local o=r(self.wanted,t,a,e);
+if n and o then
+d(self.wanted,t,a,e,n,true);
+return true;
+end
 if not self.server then self:adddefaultnameservers();end
-local n=_(a,e,t);
-local o=self:peek(a,e,t);
+local s=T(e,a,t);
+local o=self:peek(e,a,t);
 if o then return o;end
-local i,o=y();
+local h,o=v();
 local i={
-packet=i..n,
+packet=h..s,
 server=self.best_server,
 delay=1,
-retry=s.gettime()+self.delays[1]
+retry=i.gettime()+self.delays[1]
 };
 self.active[o]=self.active[o]or{};
-self.active[o][n]=i;
-local n=c.running();
-if n then
-l(self.wanted,t,e,a,n,true);
-end
+self.active[o][s]=i;
 local o,h=self:getsocket(i.server)
 if not o then
 return nil,h;
 end
 o:send(i.packet)
-if q and self.timeout then
-local r=#self.server;
+if n then
+d(self.wanted,t,a,e,n,true);
+end
+if k and self.timeout then
+local d=#self.server;
 local s=1;
-q.add_task(self.timeout,function()
-if d(self.wanted,t,e,a,n)then
-if s<r then
+k.add_task(self.timeout,function()
+if r(self.wanted,t,a,e,n)then
+if s<d then
 s=s+1;
 self:servfail(o);
 i.server=self.best_server;
@@ -1284,19 +1423,19 @@
 return self.timeout;
 end
 end
-self:cancel(t,e,a,n,true);
+self:cancel(t,a,e);
 end
 end)
 end
 return true;
 end
-function e:servfail(e)
-local t=self.socketset[e]
-self:voidsocket(e);
-self.time=s.gettime();
-for e,a in o(self.active)do
+function e:servfail(t,n)
+local h=self.socketset[t]
+t=self:voidsocket(t);
+self.time=i.gettime();
+for i,a in o(self.active)do
 for o,e in o(a)do
-if e.server==t then
+if e.server==h then
 e.server=e.server+1
 if e.server>#self.server then
 e.server=1;
@@ -1305,24 +1444,28 @@
 if e.retries>=#self.server then
 a[o]=nil;
 else
-local t=self:getsocket(e.server);
+t,n=self:getsocket(e.server);
 if t then t:send(e.packet);end
 end
 end
 end
-end
-if t==self.best_server then
+if s(a)==nil then
+self.active[i]=nil;
+end
+end
+if h==self.best_server then
 self.best_server=self.best_server+1;
 if self.best_server>#self.server then
 self.best_server=1;
 end
 end
+return t,n;
 end
 function e:settimeout(e)
 self.timeout=e;
 end
 function e:receive(t)
-self.time=s.gettime();
+self.time=i.gettime();
 t=t or self.socket;
 local e;
 for a,t in o(t)do
@@ -1339,16 +1482,15 @@
 end
 local t=self.active[e.header.id];
 t[e.question.raw]=nil;
-if not h(t)then self.active[e.header.id]=nil;end
-if not h(self.active)then self:closeall();end
+if not s(t)then self.active[e.header.id]=nil;end
+if not s(self.active)then self:closeall();end
 local e=e.question[1];
-local t=d(self.wanted,e.class,e.type,e.name);
+local t=r(self.wanted,e.class,e.type,e.name);
 if t then
-for t in o(t)do
-l(self.yielded,t,e.class,e.type,e.name,nil);
-if c.status(t)=="suspended"then c.resume(t);end
-end
-l(self.wanted,e.class,e.type,e.name,nil);
+for e in o(t)do
+if l.status(e)=="suspended"then l.resume(e);end
+end
+d(self.wanted,e.class,e.type,e.name,nil);
 end
 end
 end
@@ -1357,7 +1499,7 @@
 return e;
 end
 function e:feed(a,e,t)
-self.time=s.gettime();
+self.time=i.gettime();
 local e=self:decode(e,t);
 if e and self.active[e.header.id]
 and self.active[e.header.id][e.question.raw]then
@@ -1366,37 +1508,36 @@
 end
 local t=self.active[e.header.id];
 t[e.question.raw]=nil;
-if not h(t)then self.active[e.header.id]=nil;end
-if not h(self.active)then self:closeall();end
+if not s(t)then self.active[e.header.id]=nil;end
+if not s(self.active)then self:closeall();end
 local e=e.question[1];
 if e then
-local t=d(self.wanted,e.class,e.type,e.name);
+local t=r(self.wanted,e.class,e.type,e.name);
 if t then
-for t in o(t)do
-l(self.yielded,t,e.class,e.type,e.name,nil);
-if c.status(t)=="suspended"then c.resume(t);end
-end
-l(self.wanted,e.class,e.type,e.name,nil);
+for e in o(t)do
+if l.status(e)=="suspended"then l.resume(e);end
+end
+d(self.wanted,e.class,e.type,e.name,nil);
 end
 end
 end
 return e;
 end
-function e:cancel(i,t,o,e,a)
-local t=d(self.wanted,i,t,o);
-if t then
-if a then
-c.resume(e);
-end
-t[e]=nil;
+function e:cancel(t,a,i)
+local e=r(self.wanted,t,a,i);
+if e then
+for e in o(e)do
+if l.status(e)=="suspended"then l.resume(e);end
+end
+d(self.wanted,t,a,i,nil);
 end
 end
 function e:pulse()
 while self:receive()do end
-if not h(self.active)then return nil;end
-self.time=s.gettime();
-for i,t in o(self.active)do
-for a,e in o(t)do
+if not s(self.active)then return nil;end
+self.time=i.gettime();
+for a,t in o(self.active)do
+for o,e in o(t)do
 if self.time>=e.retry then
 e.server=e.server+1;
 if e.server>#self.server then
@@ -1404,9 +1545,9 @@
 e.delay=e.delay+1;
 end
 if e.delay>#self.delays then
-t[a]=nil;
-if not h(t)then self.active[i]=nil;end
-if not h(self.active)then return nil;end
+t[o]=nil;
+if not s(t)then self.active[a]=nil;end
+if not s(self.active)then return nil;end
 else
 local t=self.socket[e.server];
 if t then t:send(e.packet);end
@@ -1415,22 +1556,22 @@
 end
 end
 end
-if h(self.active)then return true;end
+if s(self.active)then return true;end
 return nil;
 end
 function e:lookup(o,a,t)
 self:query(o,a,t)
 while self:pulse()do
 local e={}
-for a,t in p(self.socket)do
-e[a]=t
-end
-s.select(e,nil,4)
+for t,a in c(self.socket)do
+e[t]=a
+end
+i.select(e,nil,4)
 end
 return self:peek(o,a,t);
 end
-function e:lookupex(o,a,t,e)
-return self:peek(a,t,e)or self:query(a,t,e);
+function e:lookupex(o,t,e,a)
+return self:peek(t,e,a)or self:query(t,e,a);
 end
 function e:tohostname(e)
 return t.lookup(e:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)","%4.%3.%2.%1.in-addr.arpa."),"PTR");
@@ -1450,38 +1591,38 @@
 local function n(t,e)
 return(i[e]and i[e][t[e]])or'';
 end
-function e.print(t)
-for o,e in o{'id','qr','opcode','aa','tc','rd','ra','z',
+function e.print(e)
+for o,t in o{'id','qr','opcode','aa','tc','rd','ra','z',
 'rcode','qdcount','ancount','nscount','arcount'}do
-m(a.format('%-30s','header.'..e),t.header[e],n(t.header,e));
-end
-for t,e in p(t.question)do
-m(a.format('question[%i].name         ',t),e.name);
-m(a.format('question[%i].type         ',t),e.type);
-m(a.format('question[%i].class        ',t),e.class);
+m(a.format('%-30s','header.'..t),e.header[t],n(e.header,t));
+end
+for e,t in c(e.question)do
+m(a.format('question[%i].name         ',e),t.name);
+m(a.format('question[%i].type         ',e),t.type);
+m(a.format('question[%i].class        ',e),t.class);
 end
 local h={name=1,type=1,class=1,ttl=1,rdlength=1,rdata=1};
-local e;
+local t;
 for s,i in o({'answer','authority','additional'})do
-for s,t in o(t[i])do
+for s,e in o(e[i])do
 for h,o in o({'name','type','class','ttl','rdlength'})do
-e=a.format('%s[%i].%s',i,s,o);
-m(a.format('%-30s',e),t[o],n(t,o));
-end
-for t,o in o(t)do
-if not h[t]then
-e=a.format('%s[%i].%s',i,s,t);
-m(a.format('%-30s  %s',v(e),v(o)));
+t=a.format('%s[%i].%s',i,s,o);
+m(a.format('%-30s',t),e[o],n(e,o));
+end
+for e,o in o(e)do
+if not h[e]then
+t=a.format('%s[%i].%s',i,s,e);
+m(a.format('%-30s  %s',b(t),b(o)));
 end
 end
 end
 end
 end
 function t.resolver()
-local t={active={},cache={},unsorted={},wanted={},yielded={},best_server=1};
-r(t,e);
-r(t.cache,f);
-r(t.unsorted,{__mode='kv'});
+local t={active={},cache={},unsorted={},wanted={},best_server=1};
+u(t,e);
+u(t.cache,y);
+u(t.unsorted,{__mode='kv'});
 return t;
 end
 local e=t.resolver();
@@ -1510,59 +1651,62 @@
 function t.settimeout(...)
 return e:settimeout(...);
 end
+function t.cache()
+return e.cache;
+end
 function t.socket_wrapper_set(...)
 return e:socket_wrapper_set(...);
 end
 return t;
 end)
 package.preload['net.adns']=(function(...)
-local u=require"net.server";
-local o=require"net.dns";
+local c=require"net.server";
+local a=require"net.dns";
 local e=require"util.logger".init("adns");
-local t,t=table.insert,table.remove;
-local a,s,l=coroutine,tostring,pcall;
-local function c(a,a,e,t)return(t-e)+1;end
-module"adns"
-function lookup(d,t,h,r)
-return a.wrap(function(i)
+local o,r,l=coroutine,tostring,pcall;
+local function m(a,a,t,e)return(e-t)+1;end
+local t=nil;
+local function u(d,t,s,h)
+return o.wrap(function(i)
 if i then
 e("debug","Records for %s already cached, using those...",t);
 d(i);
 return;
 end
-e("debug","Records for %s not in cache, sending query (%s)...",t,s(a.running()));
-local n,i=o.query(t,h,r);
+e("debug","Records for %s not in cache, sending query (%s)...",t,r(o.running()));
+local n,i=a.query(t,s,h);
 if n then
-a.yield({r or"IN",h or"A",t,a.running()});
-e("debug","Reply for %s (%s)",t,s(a.running()));
+o.yield({h or"IN",s or"A",t,o.running()});
+e("debug","Reply for %s (%s)",t,r(o.running()));
 end
 if n then
-n,i=l(d,o.peek(t,h,r));
+n,i=l(d,a.peek(t,s,h));
 else
 e("error","Error sending DNS query: %s",i);
 n,i=l(d,nil,i);
 end
 if not n then
-e("error","Error in DNS response handler: %s",s(i));
-end
-end)(o.peek(t,h,r));
-end
-function cancel(t,a,i)
-e("warn","Cancelling DNS lookup for %s",s(t[3]));
-o.cancel(t[1],t[2],t[3],t[4],a);
-end
-function new_async_socket(a,i)
-local s="<unknown>";
-local n={};
+e("error","Error in DNS response handler: %s",r(i));
+end
+end)(a.peek(t,s,h));
+end
+local function d(t,o,i)
+e("warn","Cancelling DNS lookup for %s",r(t[3]));
+a.cancel(t[1],t[2],t[3],t[4],o);
+end
+local function r(o,i)
+local n="<unknown>";
+local s={};
 local t={};
-function n.onincoming(a,e)
+local h;
+function s.onincoming(o,e)
 if e then
-o.feed(t,e);
-end
-end
-function n.ondisconnect(o,a)
+a.feed(t,e);
+end
+end
+function s.ondisconnect(o,a)
 if a then
-e("warn","DNS socket for %s disconnected: %s",s,a);
+e("warn","DNS socket for %s disconnected: %s",n,a);
 local t=i.server;
 if i.socketset[o]==i.best_server and i.best_server==#t then
 e("error","Exhausted all %d configured DNS servers, next lookup will try %s again",#t,t[1]);
@@ -1570,165 +1714,190 @@
 i:servfail(o);
 end
 end
-t=u.wrapclient(a,"dns",53,n);
+t,h=c.wrapclient(o,"dns",53,s);
 if not t then
-e("warn","handler is nil");
+return nil,h;
 end
 t.settimeout=function()end
-t.setsockname=function(e,...)return a:setsockname(...);end
-t.setpeername=function(o,...)s=(...);local e=a:setpeername(...);o:set_send(c);return e;end
-t.connect=function(e,...)return a:connect(...)end
-t.send=function(t,o)
-local t=a.getpeername;
-e("debug","Sending DNS query to %s",(t and t(a))or"<unconnected>");
-return a:send(o);
+t.setsockname=function(e,...)return o:setsockname(...);end
+t.setpeername=function(e,...)n=(...);local a,o=o:setpeername(...);e:set_send(m);return a,o;end
+t.connect=function(e,...)return o:connect(...)end
+t.send=function(a,t)
+e("debug","Sending DNS query to %s",n);
+return o:send(t);
 end
 return t;
 end
-o.socket_wrapper_set(new_async_socket);
-return _M;
+a.socket_wrapper_set(r);
+return{
+lookup=u;
+cancel=d;
+new_async_socket=r;
+};
 end)
 package.preload['net.server']=(function(...)
-local s=function(e)
+local c=function(e)
 return _G[e]
 end
-local ce=function(e)
-for t,a in pairs(e)do
-e[t]=nil
-end
-end
-local F,e=require("util.logger").init("socket"),table.concat;
-local i=function(...)return F("debug",e{...});end
-local de=function(...)return F("warn",e{...});end
-local e=collectgarbage
-local le=1
-local M=s"type"
-local N=s"pairs"
-local ue=s"ipairs"
-local v=s"tonumber"
-local l=s"tostring"
-local e=s"collectgarbage"
-local o=s"os"
-local a=s"table"
-local t=s"string"
-local e=s"coroutine"
-local W=o.difftime
-local Y=math.min
-local ae=math.huge
-local X=a.concat
-local a=a.remove
-local ne=t.len
-local be=t.sub
-local pe=e.wrap
-local ve=e.yield
-local k=s"ssl"
-local S=s"socket"or require"socket"
-local K=S.gettime
-local ye=(k and k.wrap)
-local we=S.bind
-local fe=S.sleep
-local me=S.select
-local e=(k and k.newcontext)
-local J
+local Y,e=require("util.logger").init("socket"),table.concat;
+local i=function(...)return Y("debug",e{...});end
+local L=function(...)return Y("warn",e{...});end
+local he=1
+local w=c"type"
+local D=c"pairs"
+local ee=c"ipairs"
+local f=c"tonumber"
+local r=c"tostring"
+local a=c"table"
+local t=c"string"
+local e=c"coroutine"
+local Z=math.min
+local ue=math.huge
+local we=a.concat
+local fe=t.sub
+local me=e.wrap
+local ye=e.yield
+local U,e=pcall(require,"ssl")
+local p=c"socket"or require"socket"
+local X=p.gettime
+local pe=p.dns.getaddrinfo
+local be=(U and e.wrap)
+local de=p.bind
+local ve=p.sleep
+local ce=p.select
+local V
+local P
+local le
+local B
+local K
+local m
+local re
+local oe
+local ae
+local te
+local ie
 local G
-local re
+local d
+local se
 local Q
-local B
-local he
-local f
-local se
-local ee
-local te
-local Z
-local P
+local ne
+local v
 local h
-local oe
-local e
-local C
-local ie
-local p
-local d
-local U
-local r
-local n
-local I
+local F
+local l
+local s
+local _
 local b
-local w
-local m
+local y
+local k
+local x
 local a
 local o
 local g
-local H
-local R
-local _
+local W
+local M
+local I
+local O
+local j
+local A
+local J
+local n
+local E
 local T
-local V
-local u
-local O
-local A
-local E
+local N
+local H
+local S
+local C
+local q
 local z
-local x
-local L
-local D
-local q
-local j
-p={}
-d={}
-r={}
-U={}
-n={}
+local R
+v={}
+h={}
+l={}
+F={}
+s={}
 b={}
-w={}
-I={}
+y={}
+_={}
+k={}
 a=0
 o=0
 g=0
-H=0
-R=0
-_=1
-T=0
-O=51e3*1024
-A=25e3*1024
-E=12e5
-z=6e4
-x=6*60*60
-L=false
-q=1e3
-j=30
-te=function(m,t,y,u,p,f,c)
-c=c or q
-local s=0
-local w,e=m.onconnect,m.ondisconnect
-local v=t.accept
+W=0
+M=0
+I=1
+O=0
+j=128
+A=10
+E=51e3*1024
+T=25e3*1024
+N=30
+H=6e4
+S=6*60*60
+local e=package.config:sub(1,1)=="\\"
+z=(e and math.huge)or p._SETSIZE or 1024
+q=p._SETSIZE or 1024
+R=30
+te=function(y,t,c,u,g,w)
+if t:getfd()>=z then
+L("server.lua: Disallowed FD number: "..t:getfd())
+t:close()
+return nil,"fd-too-large"
+end
+local f=0
+local p,e=y.onconnect,y.ondisconnect
+local b=t.accept
 local e={}
 e.shutdown=function()end
 e.ssl=function()
-return f~=nil
+return w~=nil
 end
 e.sslctx=function()
-return f
+return w
 end
 e.remove=function()
-s=s-1
+f=f-1
+if e then
+e.resume()
+end
 end
 e.close=function()
-for a,e in N(n)do
-if e.serverport==u then
-e.disconnect(e,"server closed")
-e:close(true)
-end
-end
 t:close()
-o=h(r,t,o)
-a=h(d,t,a)
-n[t]=nil
+o=d(l,t,o)
+a=d(h,t,a)
+v[c..":"..u]=nil;
+s[t]=nil
 e=nil
 t=nil
 i"server.lua: closed server handler and removed sockets from list"
 end
+e.pause=function(o)
+if not e.paused then
+a=d(h,t,a)
+if o then
+s[t]=nil
+t:close()
+t=nil;
+end
+e.paused=true;
+i("server.lua: server [",c,"]:",u," paused")
+end
+end
+e.resume=function()
+if e.paused then
+if not t then
+t=de(c,u,j);
+t:settimeout(0)
+end
+a=m(h,t,a)
+s[t]=e
+k[e]=nil
+e.paused=false;
+i("server.lua: server [",c,"]:",u," resumed")
+end
+end
 e.ip=function()
-return y
+return c
 end
 e.serverport=function()
 return u
@@ -1737,82 +1906,102 @@
 return t
 end
 e.readbuffer=function()
-if s>c then
+if a>=q or o>=q then
+e.pause()
+k[e]=n
 i("server.lua: refused new client connection: server full")
 return false
 end
-local t,n=v(t)
+local t,s=b(t)
 if t then
 local o,a=t:getpeername()
-t:settimeout(0)
-local e,n,t=C(e,m,t,o,u,a,p,f)
+local e,n,t=Q(e,y,t,o,u,a,g,w)
 if t then
 return false
 end
-s=s+1
-i("server.lua: accepted new client connection from ",l(o),":",l(a)," to ",l(u))
-if w then
-return w(e);
+f=f+1
+i("server.lua: accepted new client connection from ",r(o),":",r(a)," to ",r(u))
+if p and not w then
+return p(e);
 end
 return;
-elseif n then
-i("server.lua: error with new client connection: ",l(n))
+elseif s then
+i("server.lua: error with new client connection: ",r(s))
+e.pause()
+k[e]=n
 return false
 end
 end
 return e
 end
-C=function(B,v,t,P,G,C,_,x)
+Q=function(v,f,t,O,J,I,N,g)
+if t:getfd()>=z then
+L("server.lua: Disallowed FD number: "..t:getfd())
+t:close()
+if v then
+k[v]=n
+v.pause()
+end
+return nil,nil,"fd-too-large"
+end
 t:settimeout(0)
-local y
-local E
+local w
 local z
-local N
-local S=v.onincoming
-local D=v.onstatus
-local g=v.ondisconnect
-local W=v.ondrain
+local q
+local Y
+local P=f.onincoming
+local F=f.onstatus
+local k=f.ondisconnect
+local L=f.ondrain
+local Q=f.onreadtimeout;
+local C=f.ondetach
 local p={}
-local s=0
-local V
-local F
-local M
+local u=0
+local G
+local K
+local D
 local c=0
-local q=false
-local T=false
-local Y,U=0,0
-local O=O
-local A=A
+local j=false
+local A=false
+local S,H=0,0
+local E=E
+local T=T
 local e=p
 e.dispatch=function()
-return S
+return P
 end
 e.disconnect=function()
-return g
-end
+return k
+end
+e.onreadtimeout=Q;
 e.setlistener=function(a,t)
-S=t.onincoming
-g=t.ondisconnect
-D=t.onstatus
-W=t.ondrain
+if C then
+C(a)
+end
+P=t.onincoming
+k=t.ondisconnect
+F=t.onstatus
+L=t.ondrain
+e.onreadtimeout=t.onreadtimeout
+C=t.ondetach
 end
 e.getstats=function()
-return U,Y
+return H,S
 end
 e.ssl=function()
-return N
+return Y
 end
 e.sslctx=function()
-return x
+return g
 end
 e.send=function(n,o,i,a)
-return y(t,o,i,a)
+return w(t,o,i,a)
 end
 e.receive=function(o,a)
-return E(t,o,a)
+return z(t,o,a)
 end
 e.shutdown=function(a)
-return z(t,a)
+return q(t,a)
 end
 e.setoption=function(i,a,o)
 if t.setoption then
@@ -1820,70 +2009,83 @@
 end
 return false,"setoption not implemented";
 end
-e.close=function(u,l)
+e.force_close=function(a,t)
+if u~=0 then
+i("server.lua: discarding unwritten data for ",r(O),":",r(I))
+u=0;
+end
+return a:close(t);
+end
+e.close=function(r,n)
 if not e then return true;end
-a=h(d,t,a)
+a=d(h,t,a)
 b[e]=nil
-if s~=0 then
-if not(l or F)then
+if u~=0 then
 e.sendbuffer()
-if s~=0 then
+if u~=0 then
 if e then
 e.write=nil
 end
-V=true
+G=true
 return false
 end
-else
-y(t,X(p,"",1,s),1,c)
-end
 end
 if t then
-m=z and z(t)
+x=q and q(t)
 t:close()
-o=h(r,t,o)
-n[t]=nil
+o=d(l,t,o)
+s[t]=nil
 t=nil
 else
 i"server.lua: socket already closed"
 end
 if e then
-w[e]=nil
-I[e]=nil
+y[e]=nil
+_[e]=nil
+local t=e;
 e=nil
-end
-if B then
-B.remove()
+if k then
+k(t,n or false);
+k=nil
+end
+end
+if v then
+v.remove()
 end
 i"server.lua: closed client handler and removed socket from list"
 return true
 end
+e.server=function()
+return v
+end
 e.ip=function()
-return P
+return O
 end
 e.serverport=function()
-return G
+return J
 end
 e.clientport=function()
-return C
-end
-local I=function(i,a)
-c=c+ne(a)
-if c>O then
-I[e]="send buffer exceeded"
-e.write=Q
+return I
+end
+e.port=e.clientport
+local v=function(i,a)
+if not e then return false end
+c=c+#a
+if c>E then
+_[e]="send buffer exceeded"
+e.write=B
 return false
-elseif t and not r[t]then
-o=f(r,t,o)
-end
-s=s+1
-p[s]=a
+elseif t and not l[t]then
+o=m(l,t,o)
+end
+u=u+1
+p[u]=a
 if e then
-w[e]=w[e]or u
+y[e]=y[e]or n
 end
 return true
 end
-e.write=I
+e.write=v
 e.bufferqueue=function(t)
 return p
 end
@@ -1891,34 +2093,34 @@
 return t
 end
 e.set_mode=function(a,t)
-_=t or _
-return _
+N=t or N
+return N
 end
 e.set_send=function(a,t)
-y=t or y
-return y
-end
-e.bufferlen=function(o,a,t)
-O=t or O
-A=a or A
-return c,A,O
+w=t or w
+return w
+end
+e.bufferlen=function(o,t,a)
+E=a or E
+T=t or T
+return c,T,E
 end
 e.lock_read=function(i,o)
 if o==true then
 local o=a
-a=h(d,t,a)
+a=d(h,t,a)
 b[e]=nil
 if a~=o then
-q=true
+j=true
 end
 elseif o==false then
-if q then
-q=false
-a=f(d,t,a)
-b[e]=u
-end
-end
-return q
+if j then
+j=false
+a=m(h,t,a)
+b[e]=n
+end
+end
+return j
 end
 e.pause=function(t)
 return t:lock_read(true);
@@ -1929,635 +2131,758 @@
 e.lock=function(i,a)
 e.lock_read(a)
 if a==true then
-e.write=Q
+e.write=B
 local a=o
-o=h(r,t,o)
-w[e]=nil
+o=d(l,t,o)
+y[e]=nil
 if o~=a then
-T=true
+A=true
 end
 elseif a==false then
-e.write=I
-if T then
-T=false
-I("")
-end
-end
-return q,T
-end
-local b=function()
-local o,t,a=E(t,_)
+e.write=v
+if A then
+A=false
+v("")
+end
+end
+return j,A
+end
+local v=function()
+local a,t,o=z(t,N)
 if not t or(t=="wantread"or t=="timeout")then
-local a=o or a or""
-local o=ne(a)
-if o>A then
-g(e,"receive buffer exceeded")
-e:close(true)
+local a=a or o or""
+local o=#a
+if o>T then
+e:close("receive buffer exceeded")
 return false
 end
-local o=o*le
-U=U+o
-R=R+o
-b[e]=u
-return S(e,a,t)
-else
-i("server.lua: client ",l(P),":",l(C)," read error: ",l(t))
-F=true
-g(e,t)
-m=e and e:close()
+local o=o*he
+H=H+o
+M=M+o
+b[e]=n
+return P(e,a,t)
+else
+i("server.lua: client ",r(O),":",r(I)," read error: ",r(t))
+K=true
+x=e and e:force_close(t)
 return false
 end
 end
-local w=function()
-local f,a,d,n,v;
-local v;
+local y=function()
+local f,a,s,h,m;
 if t then
-n=X(p,"",1,s)
-f,a,d=y(t,n,1,c)
-v=(f or d or 0)*le
-Y=Y+v
-H=H+v
-m=L and ce(p)
-else
-f,a,v=false,"closed",0;
+h=we(p,"",1,u)
+f,a,s=w(t,h,1,c)
+m=(f or s or 0)*he
+S=S+m
+W=W+m
+for e=u,1,-1 do
+p[e]=nil
+end
+else
+f,a,m=false,"unexpected close",0;
 end
 if f then
-s=0
+u=0
 c=0
-o=h(r,t,o)
-w[e]=nil
-if W then
-W(e)
-end
-m=M and e:starttls(nil)
-m=V and e:close()
+o=d(l,t,o)
+y[e]=nil
+if L then
+L(e)
+end
+x=D and e:starttls(nil)
+x=G and e:force_close()
 return true
-elseif d and(a=="timeout"or a=="wantwrite")then
-n=be(n,d+1,c)
-p[1]=n
-s=1
-c=c-d
-w[e]=u
+elseif s and(a=="timeout"or a=="wantwrite")then
+h=fe(h,s+1,c)
+p[1]=h
+u=1
+c=c-s
+y[e]=n
 return true
 else
-i("server.lua: client ",l(P),":",l(C)," write error: ",l(a))
-F=true
-g(e,a)
-m=e and e:close()
+i("server.lua: client ",r(O),":",r(I)," write error: ",r(a))
+K=true
+x=e and e:force_close(a)
 return false
 end
 end
-local u;
-function e.set_sslctx(y,t)
-x=t;
-local c,s
-u=pe(function(n)
+local c;
+function e.set_sslctx(w,t)
+g=t;
+local s,r
+c=me(function(n)
 local t
-for l=1,j do
-o=(s and h(r,n,o))or o
-a=(c and h(d,n,a))or a
-c,s=nil,nil
-m,t=n:dohandshake()
+for c=1,R do
+o=(r and d(l,n,o))or o
+a=(s and d(h,n,a))or a
+s,r=nil,nil
+c,t=n:dohandshake()
 if not t then
 i("server.lua: ssl handshake done")
-e.readbuffer=b
-e.sendbuffer=w
-m=D and D(e,"ssl-handshake-complete")
-if y.autostart_ssl and v.onconnect then
-v.onconnect(y);
-end
-a=f(d,n,a)
+e.readbuffer=v
+e.sendbuffer=y
+c=F and F(e,"ssl-handshake-complete")
+if w.autostart_ssl and f.onconnect then
+f.onconnect(w);
+if u~=0 then
+o=m(l,n,o)
+end
+end
+a=m(h,n,a)
 return true
 else
 if t=="wantwrite"then
-o=f(r,n,o)
-s=true
+o=m(l,n,o)
+r=true
 elseif t=="wantread"then
-a=f(d,n,a)
-c=true
+a=m(h,n,a)
+s=true
 else
 break;
 end
 t=nil;
-ve()
-end
-end
-i("server.lua: ssl handshake error: ",l(t or"handshake too long"))
-g(e,"ssl handshake failed")
-m=e and e:close(true)
-return false
+ye()
+end
+end
+t="ssl handshake error: "..(t or"handshake too long");
+i("server.lua: ",t);
+x=e and e:force_close(t)
+return false,t
 end
 )
 end
-if k then
-e.starttls=function(m,c)
-if c then
-e:set_sslctx(c);
-end
-if s>0 then
+if U then
+e.starttls=function(f,n)
+if n then
+e:set_sslctx(n);
+end
+if u>0 then
 i"server.lua: we need to do tls, but delaying until send buffer empty"
-M=true
+D=true
 return
 end
-i("server.lua: attempting to start tls on "..l(t))
-local s,c=t
-t,c=ye(t,x)
+i("server.lua: attempting to start tls on "..r(t))
+local n,u=t
+t,u=be(t,g)
 if not t then
-i("server.lua: error while starting tls on client: ",l(c or"unknown error"))
-return nil,c
+i("server.lua: error while starting tls on client: ",r(u or"unknown error"))
+return nil,u
 end
 t:settimeout(0)
-y=t.send
-E=t.receive
-z=J
-n[t]=e
-a=f(d,t,a)
-a=h(d,s,a)
-o=h(r,s,o)
-n[s]=nil
+w=t.send
+z=t.receive
+q=V
+s[t]=e
+a=m(h,t,a)
+a=d(h,n,a)
+o=d(l,n,o)
+s[n]=nil
 e.starttls=nil
-M=nil
-N=true
-e.readbuffer=u
-e.sendbuffer=u
-u(t)
-end
-e.readbuffer=b
-e.sendbuffer=w
-if x then
+D=nil
+Y=true
+e.readbuffer=c
+e.sendbuffer=c
+return c(t)
+end
+end
+e.readbuffer=v
+e.sendbuffer=y
+w=t.send
+z=t.receive
+q=(Y and V)or t.shutdown
+s[t]=e
+a=m(h,t,a)
+if g and U then
 i"server.lua: auto-starting ssl negotiation..."
 e.autostart_ssl=true;
-e:starttls(x);
-end
-else
-e.readbuffer=b
-e.sendbuffer=w
-end
-y=t.send
-E=t.receive
-z=(N and J)or t.shutdown
-n[t]=e
-a=f(d,t,a)
+local e,t=e:starttls(g);
+if e==false then
+return nil,nil,t
+end
+end
 return e,t
 end
-J=function()
-end
-Q=function()
+V=function()
+end
+B=function()
 return false
 end
-f=function(t,a,e)
-if not t[a]then
+m=function(a,t,e)
+if not a[t]then
 e=e+1
-t[e]=a
-t[a]=e
+a[e]=t
+a[t]=e
 end
 return e;
 end
-h=function(e,o,t)
-local i=e[o]
+d=function(e,a,t)
+local i=e[a]
 if i then
-e[o]=nil
-local a=e[t]
+e[a]=nil
+local o=e[t]
 e[t]=nil
-if a~=o then
-e[a]=i
-e[i]=a
+if o~=a then
+e[o]=i
+e[i]=o
 end
 return t-1
 end
 return t
 end
-P=function(e)
-o=h(r,e,o)
-a=h(d,e,a)
-n[e]=nil
+G=function(e)
+o=d(l,e,o)
+a=d(h,e,a)
+s[e]=nil
 e:close()
 end
-local function c(a,t,o)
-local e;
+local function x(e,t,o)
+local a;
 local i=t.sendbuffer;
 function t.sendbuffer()
 i();
-if e and t.bufferlen()<o then
-a:lock_read(false);
-e=nil;
-end
-end
-local i=a.readbuffer;
-function a.readbuffer()
+if a and t.bufferlen()<o then
+e:lock_read(false);
+a=nil;
+end
+end
+local i=e.readbuffer;
+function e.readbuffer()
 i();
-if not e and t.bufferlen()>=o then
-e=true;
-a:lock_read(true);
-end
-end
-end
-se=function(t,e,r,l,h)
+if not a and t.bufferlen()>=o then
+a=true;
+e:lock_read(true);
+end
+end
+e:set_mode("*a");
+end
+re=function(e,t,d,l,r)
+e=e or"*"
 local o
-if M(r)~="table"then
+if w(d)~="table"then
 o="invalid listener table"
-end
-if M(e)~="number"or not(e>=0 and e<=65535)then
+elseif w(e)~="string"then
+o="invalid address"
+elseif w(t)~="number"or not(t>=0 and t<=65535)then
 o="invalid port"
-elseif p[t..":"..e]then
-o="listeners on '["..t.."]:"..e.."' already exist"
-elseif h and not k then
+elseif v[e..":"..t]then
+o="listeners on '["..e.."]:"..t.."' already exist"
+elseif r and not U then
 o="luasec not found"
 end
 if o then
-de("server.lua, [",t,"]:",e,": ",o)
+L("server.lua, [",e,"]:",t,": ",o)
 return nil,o
 end
-t=t or"*"
-local o,s=we(t,e)
-if s then
-de("server.lua, [",t,"]:",e,": ",s)
-return nil,s
-end
-local s,r=te(r,o,t,e,l,h,q)
-if not s then
+local o,n=de(e,t,j)
+if n then
+L("server.lua, [",e,"]:",t,": ",n)
+return nil,n
+end
+local n,d=te(d,o,e,t,l,r)
+if not n then
 o:close()
-return nil,r
+return nil,d
 end
 o:settimeout(0)
-a=f(d,o,a)
-p[t..":"..e]=s
-n[o]=s
-i("server.lua: new "..(h and"ssl "or"").."server listener on '[",t,"]:",e,"'")
-return s
-end
-ee=function(e,t)
-return p[e..":"..t];
-end
-oe=function(e,t)
-local a=p[e..":"..t]
+a=m(h,o,a)
+v[e..":"..t]=n
+s[o]=n
+i("server.lua: new "..(r and"ssl "or"").."server listener on '[",e,"]:",t,"'")
+return n
+end
+ae=function(t,e)
+return v[t..":"..e];
+end
+se=function(t,e)
+local a=v[t..":"..e]
 if not a then
-return nil,"no server found on '["..e.."]:"..l(t).."'"
+return nil,"no server found on '["..t.."]:"..r(e).."'"
 end
 a:close()
-p[e..":"..t]=nil
+v[t..":"..e]=nil
 return true
 end
-he=function()
-for e,t in N(n)do
-t:close()
-n[e]=nil
+K=function()
+for t,e in D(s)do
+e:close()
+s[t]=nil
 end
 a=0
 o=0
 g=0
-p={}
-d={}
-r={}
-U={}
-n={}
-end
-Z=function()
-return _,T,O,A,E,z,x,L,q,j
-end
-ie=function(e)
-if M(e)~="table"then
+v={}
+h={}
+l={}
+F={}
+s={}
+end
+ie=function()
+return{
+select_timeout=I;
+select_sleep_time=O;
+tcp_backlog=j;
+max_send_buffer_size=E;
+max_receive_buffer_size=T;
+select_idle_check_interval=N;
+send_timeout=H;
+read_timeout=S;
+max_connections=q;
+max_ssl_handshake_roundtrips=R;
+highest_allowed_fd=z;
+accept_retry_interval=A;
+}
+end
+ne=function(e)
+if w(e)~="table"then
 return nil,"invalid settings table"
 end
-_=v(e.timeout)or _
-T=v(e.sleeptime)or T
-O=v(e.maxsendlen)or O
-A=v(e.maxreadlen)or A
-E=v(e.checkinterval)or E
-z=v(e.sendtimeout)or z
-x=v(e.readtimeout)or x
-L=e.cleanqueue
-q=e._maxclientsperserver or q
-j=e._maxsslhandshake or j
+I=f(e.select_timeout)or I
+O=f(e.select_sleep_time)or O
+E=f(e.max_send_buffer_size)or E
+T=f(e.max_receive_buffer_size)or T
+N=f(e.select_idle_check_interval)or N
+j=f(e.tcp_backlog)or j
+H=f(e.send_timeout)or H
+S=f(e.read_timeout)or S
+A=f(e.accept_retry_interval)or A
+q=e.max_connections or q
+R=e.max_ssl_handshake_roundtrips or R
+z=e.highest_allowed_fd or z
 return true
 end
-B=function(e)
-if M(e)~="function"then
+oe=function(e)
+if w(e)~="function"then
 return nil,"invalid listener function"
 end
 g=g+1
-U[g]=e
+F[g]=e
 return true
 end
-re=function()
-return R,H,a,o,g
+le=function()
+return M,W,a,o,g
 end
 local t;
-local function y(e)
+local function f(e)
 t=not not e;
 end
-G=function(a)
+P=function(a)
 if t then return"quitting";end
 if a then t="once";end
-local e=ae;
+local e=ue;
 repeat
-local a,o,s=me(d,r,Y(_,e))
-for t,e in ue(o)do
-local t=n[e]
-if t then
-t.sendbuffer()
-else
-P(e)
+local o,a,h=ce(h,l,Z(I,e))
+for e,t in ee(a)do
+local e=s[t]
+if e then
+e.sendbuffer()
+else
+G(t)
 i"server.lua: found no handler and closed socket (writelist)"
 end
 end
-for e,t in ue(a)do
-local e=n[t]
+for e,t in ee(o)do
+local e=s[t]
 if e then
 e.readbuffer()
 else
-P(t)
+G(t)
 i"server.lua: found no handler and closed socket (readlist)"
 end
 end
-for e,t in N(I)do
+for e,t in D(_)do
 e.disconnect()(e,t)
-e:close(true)
-end
-ce(I)
-u=K()
-if u-D>=Y(e,1)then
-e=ae;
+e:force_close()
+_[e]=nil;
+end
+n=X()
+if n-J>N then
+J=n
+for e,t in D(y)do
+if n-t>H then
+e.disconnect()(e,"send timeout")
+e:force_close()
+end
+end
+for e,t in D(b)do
+if n-t>S then
+if not(e.onreadtimeout)or e:onreadtimeout()~=true then
+e.disconnect()(e,"read timeout")
+e:close()
+else
+b[e]=n
+end
+end
+end
+end
+if n-C>=Z(e,1)then
+e=ue;
 for t=1,g do
-local t=U[t](u)
-if t then e=Y(e,t);end
-end
-D=u
-else
-e=e-(u-D);
-end
-fe(T)
+local t=F[t](n)
+if t then e=Z(e,t);end
+end
+C=n
+else
+e=e-(n-C);
+end
+for e,t in D(k)do
+if n-t>A then
+k[e]=nil;
+e.resume();
+end
+end
+ve(O)
 until t;
 if a and t=="once"then t=nil;return;end
+K();
 return"quitting"
 end
-local function l()
-return G(true);
-end
-local function p()
+local function u()
+return P(true);
+end
+local function d()
 return"select";
 end
-local i=function(t,d,s,a,e,i)
-local e=C(nil,a,t,d,s,"clientport",e,i)
-n[t]=e
+local i=function(e,a,h,t,n,i)
+local e,a,n=Q(nil,t,e,a,h,"clientport",n,i)
+if not e then return nil,n end
+s[a]=e
 if not i then
-o=f(r,t,o)
-if a.onconnect then
-local i=e.sendbuffer;
+o=m(l,a,o)
+if t.onconnect then
+local a=e.sendbuffer;
 e.sendbuffer=function()
-o=h(r,t,o);
-e.sendbuffer=i;
-a.onconnect(e);
-if#e:bufferqueue()>0 then
-return i();
-end
-end
-end
-end
-return e,t
-end
-local t=function(o,a,n,h,r)
-local e,t=S.tcp()
-if t then
-return nil,t
+e.sendbuffer=a;
+t.onconnect(e);
+return a();
+end
+end
+end
+return e,a
+end
+local o=function(o,t,s,r,n,a)
+local e
+if w(s)~="table"then
+e="invalid listener table"
+elseif w(o)~="string"then
+e="invalid address"
+elseif w(t)~="number"or not(t>=0 and t<=65535)then
+e="invalid port"
+elseif n and not U then
+e="luasec not found"
+end
+if not a then
+local e,t=pe(o)
+if not e then return nil,t end
+if e[1]and e[1].family=="inet6"then
+a="tcp6"
+else
+a="tcp"
+end
+end
+local a=p[a]
+if w(a)~="function"then
+e="invalid socket type"
+end
+if e then
+L("server.lua, addclient: ",e)
+return nil,e
+end
+local e,a=a()
+if a then
+return nil,a
 end
 e:settimeout(0)
-m,t=e:connect(o,a)
-if t then
-local e=i(e,o,a,n)
-else
-C(nil,n,e,o,a,"clientport",h,r)
-end
-end
-s"setmetatable"(n,{__mode="k"})
-s"setmetatable"(b,{__mode="k"})
-s"setmetatable"(w,{__mode="k"})
-D=K()
-V=K()
-B(function()
-local e=W(u-V)
-if e>E then
-V=u
-for e,t in N(w)do
-if W(u-t)>z then
-e.disconnect()(e,"send timeout")
-e:close(true)
-end
-end
-for e,t in N(b)do
-if W(u-t)>x then
-e.disconnect()(e,"read timeout")
-e:close()
-end
-end
-end
-end
-)
+local h,a=e:connect(o,t)
+if h or a=="timeout"then
+return i(e,o,t,s,r,n)
+else
+return nil,a
+end
+end
+c"setmetatable"(s,{__mode="k"})
+c"setmetatable"(b,{__mode="k"})
+c"setmetatable"(y,{__mode="k"})
+C=X()
+J=X()
 local function a(e)
-local t=F;
+local t=Y;
 if e then
-F=e;
+Y=e;
 end
 return t;
 end
 return{
-addclient=t,
+_addtimer=oe,
+addclient=o,
 wrapclient=i,
-loop=G,
-link=c,
-step=l,
-stats=re,
-closeall=he,
-addtimer=B,
-addserver=se,
-getserver=ee,
+loop=P,
+link=x,
+step=u,
+stats=le,
+closeall=K,
+addserver=re,
+getserver=ae,
 setlogger=a,
-getsettings=Z,
-setquitting=y,
-removeserver=oe,
-get_backend=p,
-changesettings=ie,
+getsettings=ie,
+setquitting=f,
+removeserver=se,
+get_backend=d,
+changesettings=ne,
 }
 end)
 package.preload['util.xmppstream']=(function(...)
 local e=require"lxp";
-local t=require"util.stanza";
-local w=t.stanza_mt;
-local a=tostring;
-local h=table.insert;
-local f=table.concat;
-local g=table.remove;
-local y=setmetatable;
-local u=require"util.logger".init("xmppstream");
-local b=pcall(e.new,{StartDoctypeDecl=false});
-if not b then
-u("warn","The version of LuaExpat on your system leaves Prosody "
-.."vulnerable to denial-of-service attacks. You should upgrade to "
-.."LuaExpat 1.1.1 or higher as soon as possible. See "
-.."http://prosody.im/doc/depends#luaexpat for more information.");
-end
-local v=error;
-module"xmppstream"
-local p=e.new;
-local k={
-["http://www.w3.org/XML/1998/namespace"]="xml";
+local b=require"util.stanza";
+local k=b.stanza_mt;
+local m=error;
+local t=tostring;
+local d=table.insert;
+local p=table.concat;
+local x=table.remove;
+local v=setmetatable;
+local q=pcall(e.new,{StartDoctypeDecl=false});
+local j=pcall(e.new,{XmlDecl=false});
+local a=not not e.new({}).getcurrentbytecount;
+local z=1024*1024*10;
+local o=nil;
+local y=e.new;
+local _={
+["http://www.w3.org/XML/1998/namespace\1lang"]="xml:lang";
+["http://www.w3.org/XML/1998/namespace\1space"]="xml:space";
+["http://www.w3.org/XML/1998/namespace\1base"]="xml:base";
+["http://www.w3.org/XML/1998/namespace\1id"]="xml:id";
 };
-local o="http://etherx.jabber.org/streams";
-local s="\1";
-local l="^([^"..s.."]*)"..s.."?(.*)$";
-_M.ns_separator=s;
-_M.ns_pattern=l;
-function new_sax_handlers(t,e)
-local i={};
-local p=t.log or u;
-local m=e.streamopened;
-local c=e.streamclosed;
-local r=e.error or function(t,e)v("XML stream error: "..a(e));end;
-local j=e.handlestanza;
-local a=e.stream_ns or o;
-local d=e.stream_tag or"stream";
-if a~=""then
-d=a..s..d;
-end
-local q=a..s..(e.error_tag or"error");
-local x=e.default_ns;
-local s={};
-local o,e={};
-local n=0;
-function i:StartElement(u,a)
-if e and#o>0 then
-h(e,f(o));
-o={};
-end
-local i,o=u:match(l);
-if o==""then
-i,o="",i;
-end
-if i~=x or n>0 then
-a.xmlns=i;
-n=n+1;
-end
-for e=1,#a do
-local t=a[e];
-a[e]=nil;
-local e,o=t:match(l);
-if o~=""then
-e=k[e];
-if e then
-a[e..":"..o]=a[t];
-a[t]=nil;
-end
+local s="http://etherx.jabber.org/streams";
+local r="\1";
+local g="^([^"..r.."]*)"..r.."?(.*)$";
+local function h()end
+local function w(i,e,n)
+local o={};
+local w=e.streamopened;
+local f=e.streamclosed;
+local l=e.error or function(o,a,e)m("XML stream error: "..t(a)..(e and": "..t(e)or""),2);end;
+local y=e.handlestanza;
+n=n or h;
+local t=e.stream_ns or s;
+local c=e.stream_tag or"stream";
+if t~=""then
+c=t..r..c;
+end
+local b=t..r..(e.error_tag or"error");
+local z=e.default_ns;
+local u={};
+local s,e={};
+local t=0;
+local h=0;
+function o:StartElement(m,o)
+if e and#s>0 then
+d(e,p(s));
+s={};
+end
+local r,s=m:match(g);
+if s==""then
+r,s="",r;
+end
+if r~=z or h>0 then
+o.xmlns=r;
+h=h+1;
+end
+for t=1,#o do
+local e=o[t];
+o[t]=nil;
+local t=_[e];
+if t then
+o[t]=o[e];
+o[e]=nil;
 end
 end
 if not e then
-if t.notopen then
-if u==d then
-n=0;
-if m then
-m(t,a);
-end
-else
-r(t,"no-stream");
+if a then
+t=self:getcurrentbytecount();
+end
+if i.notopen then
+if m==c then
+h=0;
+if w then
+if a then
+n(t);
+t=0;
+end
+w(i,o);
+end
+else
+l(i,"no-stream",m);
 end
 return;
 end
-if i=="jabber:client"and o~="iq"and o~="presence"and o~="message"then
-r(t,"invalid-top-level-element");
-end
-e=y({name=o,attr=a,tags={}},w);
-else
-h(s,e);
+if r=="jabber:client"and s~="iq"and s~="presence"and s~="message"then
+l(i,"invalid-top-level-element");
+end
+e=v({name=s,attr=o,tags={}},k);
+else
+if a then
+t=t+self:getcurrentbytecount();
+end
+d(u,e);
 local t=e;
-e=y({name=o,attr=a,tags={}},w);
-h(t,e);
-h(t.tags,e);
-end
-end
-function i:CharacterData(t)
+e=v({name=s,attr=o,tags={}},k);
+d(t,e);
+d(t.tags,e);
+end
+end
+if j then
+function o:XmlDecl(e,e,e)
+if a then
+n(self:getcurrentbytecount());
+end
+end
+end
+function o:StartCdataSection()
+if a then
 if e then
-h(o,t);
-end
-end
-function i:EndElement(a)
-if n>0 then
-n=n-1;
+t=t+self:getcurrentbytecount();
+else
+n(self:getcurrentbytecount());
+end
+end
+end
+function o:EndCdataSection()
+if a then
+if e then
+t=t+self:getcurrentbytecount();
+else
+n(self:getcurrentbytecount());
+end
+end
+end
+function o:CharacterData(o)
+if e then
+if a then
+t=t+self:getcurrentbytecount();
+end
+d(s,o);
+elseif a then
+n(self:getcurrentbytecount());
+end
+end
+function o:EndElement(o)
+if a then
+t=t+self:getcurrentbytecount()
+end
+if h>0 then
+h=h-1;
 end
 if e then
-if#o>0 then
-h(e,f(o));
-o={};
-end
-if#s==0 then
-if a~=q then
-j(t,e);
-else
-r(t,"stream-error",e);
+if#s>0 then
+d(e,p(s));
+s={};
+end
+if#u==0 then
+if a then
+n(t);
+end
+t=0;
+if o~=b then
+y(i,e);
+else
+l(i,"stream-error",e);
 end
 e=nil;
 else
-e=g(s);
-end
-else
-if a==d then
-if c then
-c(t);
-end
-else
-local a,e=a:match(l);
-if e==""then
-a,e="",a;
-end
-r(t,"parse-error","unexpected-element-close",e);
-end
-e,o=nil,{};
-s={};
+e=x(u);
+end
+else
+if f then
+f(i);
+end
 end
 end
 local function a(e)
-r(t,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1.");
+l(i,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1.");
 if not e.stop or not e:stop()then
-v("Failed to abort parsing");
-end
-end
-if b then
-i.StartDoctypeDecl=a;
-end
-i.Comment=a;
-i.ProcessingInstruction=a;
-local function n()
-e,o=nil,{};
-s={};
-end
-local function a(a,e)
-t=e;
-p=e.log or u;
-end
-return i,{reset=n,set_session=a};
-end
-function new(t,e)
-local t,o=new_sax_handlers(t,e);
-local e=p(t,s);
-local a=e.parse;
+m("Failed to abort parsing");
+end
+end
+if q then
+o.StartDoctypeDecl=a;
+end
+o.Comment=a;
+o.ProcessingInstruction=a;
+local function a()
+e,s,t=nil,{},0;
+u={};
+end
+local function t(t,e)
+i=e;
+end
+return o,{reset=a,set_session=t};
+end
+local function u(d,l,o)
+local t=0;
+local e;
+if a then
+function e(e)
+t=t-e;
+end
+o=o or z;
+elseif o then
+m("Stanza size limits are not supported on this version of LuaExpat")
+end
+local n,s=w(d,l,e);
+local i=y(n,r,false);
+local h=i.parse;
+function d.open_stream(e,a,i)
+local o=e.sends2s or e.send;
+local t={
+["xmlns:stream"]="http://etherx.jabber.org/streams",
+["xml:lang"]="en",
+xmlns=l.default_ns,
+version=e.version and(e.version>0 and"1.0"or nil),
+id=e.streamid,
+from=a or e.host,to=i,
+};
+if e.stream_attrs then
+e:stream_attrs(a,i,t)
+end
+o("<?xml version='1.0'?>");
+o(b.stanza("stream:stream",t):top_tag());
+return true;
+end
 return{
 reset=function()
-e=p(t,s);
-a=e.parse;
-o.reset();
+i=y(n,r,false);
+h=i.parse;
+t=0;
+s.reset();
 end,
-feed=function(o,t)
-return a(e,t);
+feed=function(n,e)
+if a then
+t=t+#e;
+end
+local i,e=h(i,e);
+if a and t>o then
+return nil,"stanza-too-large";
+end
+return i,e;
 end,
-set_session=o.set_session;
+set_session=s.set_session;
 };
 end
-return _M;
+return{
+ns_separator=r;
+ns_pattern=g;
+new_sax_handlers=w;
+new=u;
+};
 end)
 package.preload['util.jid']=(function(...)
-local a=string.match;
-local h=require"util.encodings".stringprep.nodeprep;
+local o=select;
+local a,i=string.match,string.sub;
+local l=require"util.encodings".stringprep.nodeprep;
 local r=require"util.encodings".stringprep.nameprep;
 local d=require"util.encodings".stringprep.resourceprep;
-local n={
+local h={
 [" "]="\\20";['"']="\\22";
 ["&"]="\\26";["'"]="\\27";
 ["/"]="\\2f";[":"]="\\3a";
@@ -2565,8 +2890,8 @@
 ["@"]="\\40";["\\"]="\\5c";
 };
 local s={};
-for e,t in pairs(n)do s[t]=e;end
-module"jid"
+for e,t in pairs(h)do s[t]=e;end
+local e=nil;
 local function t(e)
 if not e then return;end
 local i,t=a(e,"^([^@/]+)@()");
@@ -2576,157 +2901,243 @@
 if(not t)or((not a)and#e>=o)then return nil,nil,nil;end
 return i,t,a;
 end
-split=t;
-function bare(e)
+local function m(e)
 local t,e=t(e);
 if t and e then
 return t.."@"..e;
 end
 return e;
 end
-local function o(e)
-local t,a,e=t(e);
-if a then
-a=r(a);
-if not a then return;end
-if t then
-t=h(t);
-if not t then return;end
-end
-if e then
-e=d(e);
+local function n(e)
+local t,e,a=t(e);
+if e and e~="."then
+if i(e,-1,-1)=="."then
+e=i(e,1,-2);
+end
+e=r(e);
 if not e then return;end
-end
-return t,a,e;
-end
-end
-prepped_split=o;
-function prep(e)
-local a,e,t=o(e);
-if e then
+if t then
+t=l(t);
+if not t then return;end
+end
 if a then
-e=a.."@"..e;
-end
-if t then
-e=e.."/"..t;
-end
+a=d(a);
+if not a then return;end
+end
+return t,e,a;
+end
+end
+local function i(a,e,t)
+if not e then return end
+if a and t then
+return a.."@"..e.."/"..t;
+elseif a then
+return a.."@"..e;
+elseif t then
+return e.."/"..t;
 end
 return e;
 end
-function join(a,e,t)
-if a and e and t then
-return a.."@"..e.."/"..t;
-elseif a and e then
-return a.."@"..e;
-elseif e and t then
-return e.."/"..t;
-elseif e then
-return e;
-end
-return nil;
-end
-function compare(e,a)
-local i,o,n=t(e);
-local a,e,t=t(a);
-if((a~=nil and a==i)or a==nil)and
-((e~=nil and e==o)or e==nil)and
-((t~=nil and t==n)or t==nil)then
+local function c(e)
+local a,t,e=n(e);
+return i(a,t,e);
+end
+local function u(e,a)
+local o,n,i=t(e);
+local e,t,a=t(a);
+if((e~=nil and e==o)or e==nil)and
+((t~=nil and t==n)or t==nil)and
+((a~=nil and a==i)or a==nil)then
 return true
 end
 return false
 end
-function escape(e)return e and(e:gsub(".",n));end
-function unescape(e)return e and(e:gsub("\\%x%x",s));end
-return _M;
+local function l(e)
+return(o(1,t(e)));
+end
+local function d(e)
+return(o(2,t(e)));
+end
+local function r(e)
+return(o(3,t(e)));
+end
+local function a(e)return e and(e:gsub(".",h));end
+local function o(e)return e and(e:gsub("\\%x%x",s));end
+return{
+split=t;
+bare=m;
+prepped_split=n;
+join=i;
+prep=c;
+compare=u;
+node=l;
+host=d;
+resource=r;
+escape=a;
+unescape=o;
+};
 end)
 package.preload['util.events']=(function(...)
-local i=pairs;
-local h=table.insert;
-local s=table.sort;
-local r=setmetatable;
-local n=next;
-module"events"
-function new()
+local a=pairs;
+local s=table.insert;
+local l=table.remove;
+local r=table.sort;
+local d=setmetatable;
+local h=next;
+local e=nil;
+local function u()
+local o={};
+local t;
+local n={};
+local i={};
+local function u(n,o)
+local e=i[o];
+if not e or h(e)==nil then return;end
 local t={};
-local e={};
-local function o(o,a)
-local e=e[a];
-if not e or n(e)==nil then return;end
-local t={};
-for e in i(e)do
-h(t,e);
-end
-s(t,function(a,t)return e[a]>e[t];end);
-o[a]=t;
+for e in a(e)do
+s(t,e);
+end
+r(t,function(a,t)return e[a]>e[t];end);
+n[o]=t;
 return t;
 end;
-r(t,{__index=o});
-local function h(o,n,i)
-local a=e[o];
-if a then
-a[n]=i or 0;
-else
-a={[n]=i or 0};
-e[o]=a;
-end
-t[o]=nil;
+d(o,{__index=u});
+local function s(t,n,a)
+local e=i[t];
+if e then
+e[n]=a or 0;
+else
+e={[n]=a or 0};
+i[t]=e;
+end
+o[t]=nil;
 end;
-local function s(a,i)
-local o=e[a];
-if o then
-o[i]=nil;
+local function r(e,a)
+local t=i[e];
+if t then
 t[a]=nil;
-if n(o)==nil then
-e[a]=nil;
+o[e]=nil;
+if h(t)==nil then
+i[e]=nil;
 end
 end
 end;
-local function o(e)
-for t,e in i(e)do
-h(t,e);
-end
+local function m(e)
+return o[e];
 end;
-local function n(e)
-for e,t in i(e)do
+local function f(e)
+for e,t in a(e)do
 s(e,t);
 end
 end;
-local function a(e,...)
-local e=t[e];
+local function c(e)
+for t,e in a(e)do
+r(t,e);
+end
+end;
+local function h(e,t)
+local e=o[e];
 if e then
-for t=1,#e do
-local e=e[t](...);
+for a=1,#e do
+local e=e[a](t);
 if e~=nil then return e;end
 end
 end
 end;
+local function u(i,s)
+local a=n[i]or t;
+if a then
+local e=#a;
+local function n(i,o)
+e=e-1;
+if e==0 then
+if t==nil or a==t then
+return h(i,o);
+end
+a,e=t,#t;
+return a[e](n,i,o);
+else
+return a[e](n,i,o);
+end
+end
+return a[e](n,i,s);
+end
+return h(i,s);
+end
+local function d(a,o)
+local e;
+if a==false then
+e=t;
+if not e then
+e={};
+t=e;
+end
+else
+e=n[a];
+if not e then
+e={};
+n[a]=e;
+end
+end
+e[#e+1]=o;
+end
+local function h(a,o)
+local e;
+if a==false then
+e=t;
+else
+e=n[a];
+end
+if not e then return;end
+for t=#e,1 do
+if e[t]==o then
+l(e,t);
+end
+end
+if#e==0 then
+if a==false then
+t=nil;
+else
+n[a]=nil;
+end
+end
+end
 return{
-add_handler=h;
-remove_handler=s;
-add_handlers=o;
-remove_handlers=n;
-fire_event=a;
-_handlers=t;
-_event_map=e;
+add_handler=s;
+remove_handler=r;
+add_handlers=f;
+remove_handlers=c;
+get_handlers=m;
+wrappers={
+add_handler=d;
+remove_handler=h;
 };
-end
-return _M;
+add_wrapper=d;
+remove_wrapper=h;
+fire_event=u;
+_handlers=o;
+_event_map=i;
+};
+end
+return{
+new=u;
+};
 end)
 package.preload['util.dataforms']=(function(...)
 local e=setmetatable;
 local t,i=pairs,ipairs;
-local n,s,l=tostring,type,next;
+local d,r,u=tostring,type,next;
 local h=table.concat;
-local u=require"util.stanza";
-local d=require"util.jid".prep;
-module"dataforms"
+local m=require"util.stanza";
+local l=require"util.jid".prep;
+local n={};
 local c='jabber:x:data';
-local r={};
-local t={__index=r};
-function new(a)
-return e(a,t);
-end
-function from_stanza(e)
+local s={};
+local a={__index=s};
+function n.new(t)
+return e(t,a);
+end
+function n.from_stanza(e)
 local o={
 title=e:get_child_text("title");
 instructions=e:get_child_text("instructions");
@@ -2763,8 +3174,8 @@
 end
 return new(o);
 end
-function r.form(t,a,e)
-local e=u.stanza("x",{xmlns=c,type=e or"form"});
+function s.form(t,n,e)
+local e=m.stanza("x",{xmlns=c,type=e or"form"});
 if t.title then
 e:tag("title"):text(t.title):up();
 end
@@ -2772,55 +3183,55 @@
 e:tag("instructions"):text(t.instructions):up();
 end
 for t,o in i(t)do
-local t=o.type or"text-single";
-e:tag("field",{type=t,var=o.name,label=o.label});
-local a=(a and a[o.name])or o.value;
-if a then
-if t=="hidden"then
-if s(a)=="table"then
+local a=o.type or"text-single";
+e:tag("field",{type=a,var=o.name,label=o.label});
+local t=(n and n[o.name])or o.value;
+if t then
+if a=="hidden"then
+if r(t)=="table"then
 e:tag("value")
-:add_child(a)
+:add_child(t)
 :up();
 else
-e:tag("value"):text(n(a)):up();
-end
-elseif t=="boolean"then
-e:tag("value"):text((a and"1")or"0"):up();
-elseif t=="fixed"then
-elseif t=="jid-multi"then
-for a,t in i(a)do
+e:tag("value"):text(d(t)):up();
+end
+elseif a=="boolean"then
+e:tag("value"):text((t and"1")or"0"):up();
+elseif a=="fixed"then
+elseif a=="jid-multi"then
+for a,t in i(t)do
+e:tag("value"):text(t):up();
+end
+elseif a=="jid-single"then
 e:tag("value"):text(t):up();
-end
-elseif t=="jid-single"then
-e:tag("value"):text(a):up();
-elseif t=="text-single"or t=="text-private"then
-e:tag("value"):text(a):up();
-elseif t=="text-multi"then
-for t in a:gmatch("([^\r\n]+)\r?\n*")do
+elseif a=="text-single"or a=="text-private"then
+e:tag("value"):text(t):up();
+elseif a=="text-multi"then
+for t in t:gmatch("([^\r\n]+)\r?\n*")do
 e:tag("value"):text(t):up();
 end
-elseif t=="list-single"then
-local o=false;
-for a,t in i(a)do
-if s(t)=="table"then
+elseif a=="list-single"then
+local a=false;
+for o,t in i(t)do
+if r(t)=="table"then
 e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up();
-if t.default and(not o)then
+if t.default and(not a)then
 e:tag("value"):text(t.value):up();
-o=true;
-end
-else
-e:tag("option",{label=t}):tag("value"):text(n(t)):up():up();
-end
-end
-elseif t=="list-multi"then
-for a,t in i(a)do
-if s(t)=="table"then
+a=true;
+end
+else
+e:tag("option",{label=t}):tag("value"):text(d(t)):up():up();
+end
+end
+elseif a=="list-multi"then
+for a,t in i(t)do
+if r(t)=="table"then
 e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up();
 if t.default then
 e:tag("value"):text(t.value):up();
 end
 else
-e:tag("option",{label=t}):tag("value"):text(n(t)):up():up();
+e:tag("option",{label=t}):tag("value"):text(d(t)):up():up();
 end
 end
 end
@@ -2833,32 +3244,32 @@
 return e;
 end
 local e={};
-function r.data(t,s)
-local n={};
+function s.data(t,n)
+local o={};
 local a={};
-for o,t in i(t)do
-local o;
-for e in s:childtags()do
+for i,t in i(t)do
+local i;
+for e in n:childtags()do
 if t.name==e.attr.var then
-o=e;
+i=e;
 break;
 end
 end
-if not o then
+if not i then
 if t.required then
 a[t.name]="Required value missing";
 end
 else
 local e=e[t.type];
 if e then
-n[t.name],a[t.name]=e(o,t.required);
-end
-end
-end
-if l(a)then
-return n,a;
-end
-return n;
+o[t.name],a[t.name]=e(i,t.required);
+end
+end
+end
+if u(a)then
+return o,a;
+end
+return o;
 end
 e["text-single"]=
 function(t,a)
@@ -2873,12 +3284,12 @@
 e["text-single"];
 e["jid-single"]=
 function(t,o)
-local a=t:get_child_text("value")
-local t=d(a);
-if t and#t>0 then
-return t
-elseif a then
-return nil,"Invalid JID: "..a;
+local t=t:get_child_text("value")
+local a=l(t);
+if a and#a>0 then
+return a
+elseif t then
+return nil,"Invalid JID: "..t;
 elseif o then
 return nil,"Required value missing";
 end
@@ -2889,7 +3300,7 @@
 local t={};
 for e in o:childtags("value")do
 local e=e:get_text();
-local o=d(e);
+local o=l(e);
 a[#a+1]=o;
 if e and not o then
 t[#t+1]=("Invalid JID: "..e);
@@ -2902,12 +3313,12 @@
 end
 end
 e["list-multi"]=
-function(a,o)
+function(o,a)
 local t={};
-for e in a:childtags("value")do
+for e in o:childtags("value")do
 t[#t+1]=e:get_text();
 end
-return t,(o and#t==0 and"Required value missing"or nil);
+return t,(a and#t==0 and"Required value missing"or nil);
 end
 e["text-multi"]=
 function(t,a)
@@ -2939,24 +3350,24 @@
 function(e)
 return e:get_child_text("value");
 end
-return _M;
+return n;
 end)
 package.preload['util.caps']=(function(...)
-local l=require"util.encodings".base64.encode;
-local d=require"util.hashes".sha1;
-local n,h,s=table.insert,table.sort,table.concat;
+local d=require"util.encodings".base64.encode;
+local l=require"util.hashes".sha1;
+local n,s,h=table.insert,table.sort,table.concat;
 local r=ipairs;
-module"caps"
-function calculate_hash(e)
-local a,o,i={},{},{};
+local e=nil;
+local function u(e)
+local a,i,o={},{},{};
 for t,e in r(e)do
 if e.name=="identity"then
 n(a,(e.attr.category or"").."\0"..(e.attr.type or"").."\0"..(e.attr["xml:lang"]or"").."\0"..(e.attr.name or""));
 elseif e.name=="feature"then
-n(o,e.attr.var or"");
+n(i,e.attr.var or"");
 elseif e.name=="x"and e.attr.xmlns=="jabber:x:data"then
 local t={};
-local o;
+local i;
 for a,e in r(e.tags)do
 if e.name=="field"and e.attr.var then
 local a={};
@@ -2964,41 +3375,43 @@
 e=#e.tags==0 and e:get_text();
 if e then n(a,e);end
 end
-h(a);
+s(a);
 if e.attr.var=="FORM_TYPE"then
-o=a[1];
+i=a[1];
 elseif#a>0 then
-n(t,e.attr.var.."\0"..s(a,"<"));
+n(t,e.attr.var.."\0"..h(a,"<"));
 else
 n(t,e.attr.var);
 end
 end
 end
-h(t);
-t=s(t,"<");
-if o then t=o.."\0"..t;end
-n(i,t);
-end
-end
-h(a);
-h(o);
-h(i);
-if#a>0 then a=s(a,"<"):gsub("%z","/").."<";else a="";end
-if#o>0 then o=s(o,"<").."<";else o="";end
-if#i>0 then i=s(i,"<"):gsub("%z","<").."<";else i="";end
-local e=a..o..i;
-local t=l(d(e));
+s(t);
+t=h(t,"<");
+if i then t=i.."\0"..t;end
+n(o,t);
+end
+end
+s(a);
+s(i);
+s(o);
+if#a>0 then a=h(a,"<"):gsub("%z","/").."<";else a="";end
+if#i>0 then i=h(i,"<").."<";else i="";end
+if#o>0 then o=h(o,"<"):gsub("%z","<").."<";else o="";end
+local e=a..i..o;
+local t=d(l(e));
 return t,e;
 end
-return _M;
+return{
+calculate_hash=u;
+};
 end)
 package.preload['util.vcard']=(function(...)
 local o=require"util.stanza";
 local a,u=table.insert,table.concat;
 local h=type;
-local e,r,f=next,pairs,ipairs;
-local m,c,d,l;
-local w="\n";
+local e,s,m=next,pairs,ipairs;
+local c,d,l,r;
+local f="\n";
 local i;
 local function e()
 error"Not implemented"
@@ -3006,10 +3419,10 @@
 local function e()
 error"Not implemented"
 end
-local function y(e)
+local function w(e)
 return e:gsub("[,:;\\]","\\%1"):gsub("\n","\\n");
 end
-local function s(e)
+local function n(e)
 return e:gsub("\\?[\\nt:;,]",{
 ["\\\\"]="\\",
 ["\\n"]="\n",
@@ -3023,64 +3436,64 @@
 [","]="\31",
 });
 end
-local function p(e)
-local a=o.stanza(e.name,{xmlns="vcard-temp"});
-local t=i[e.name];
-if t=="text"then
-a:text(e[1]);
-elseif h(t)=="table"then
-if t.types and e.TYPE then
-if h(e.TYPE)=="table"then
-for o,t in r(t.types)do
-for o,e in r(e.TYPE)do
-if e:upper()==t then
-a:tag(t):up();
+local function y(t)
+local a=o.stanza(t.name,{xmlns="vcard-temp"});
+local e=i[t.name];
+if e=="text"then
+a:text(t[1]);
+elseif h(e)=="table"then
+if e.types and t.TYPE then
+if h(t.TYPE)=="table"then
+for o,e in s(e.types)do
+for o,t in s(t.TYPE)do
+if t:upper()==e then
+a:tag(e):up();
 break;
 end
 end
 end
 else
-a:tag(e.TYPE:upper()):up();
-end
-end
-if t.props then
-for o,t in r(t.props)do
-if e[t]then
-a:tag(t):up();
-end
-end
-end
-if t.value then
-a:tag(t.value):text(e[1]):up();
-elseif t.values then
-local o=t.values;
+a:tag(t.TYPE:upper()):up();
+end
+end
+if e.props then
+for o,e in s(e.props)do
+if t[e]then
+a:tag(e):up();
+end
+end
+end
+if e.value then
+a:tag(e.value):text(t[1]):up();
+elseif e.values then
+local o=e.values;
 local i=o.behaviour=="repeat-last"and o[#o];
-for o=1,#e do
-a:tag(t.values[o]or i):text(e[o]):up();
+for o=1,#t do
+a:tag(e.values[o]or i):text(t[o]):up();
 end
 end
 end
 return a;
 end
-local function n(t)
-local e=o.stanza("vCard",{xmlns="vcard-temp"});
-for a=1,#t do
-e:add_child(p(t[a]));
-end
-return e;
-end
-function l(e)
-if not e[1]or e[1].name then
-return n(e)
-else
-local t=o.stanza("xCard",{xmlns="vcard-temp"});
+local function t(e)
+local t=o.stanza("vCard",{xmlns="vcard-temp"});
 for a=1,#e do
-t:add_child(n(e[a]));
+t:add_child(y(e[a]));
 end
 return t;
 end
-end
-function m(t)
+function r(e)
+if not e[1]or e[1].name then
+return t(e)
+else
+local a=o.stanza("xCard",{xmlns="vcard-temp"});
+for o=1,#e do
+a:add_child(t(e[o]));
+end
+return a;
+end
+end
+function c(t)
 t=t
 :gsub("\r\n","\n")
 :gsub("\n ","")
@@ -3088,19 +3501,19 @@
 local h={};
 local e;
 for t in t:gmatch("[^\n]+")do
-local t=s(t);
+local t=n(t);
 local s,t,n=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$");
 n=n:gsub("\29",":");
 if#t>0 then
 local a={};
-for e,i,o in t:gmatch("\30([^=]+)(=?)([^\30]*)")do
+for e,o,i in t:gmatch("\30([^=]+)(=?)([^\30]*)")do
 e=e:upper();
 local t={};
-for e in o:gmatch("[^\31]+")do
+for e in i:gmatch("[^\31]+")do
 t[#t+1]=e
 t[e]=true;
 end
-if i=="="then
+if o=="="then
 a[e]=t;
 else
 a[e]=true;
@@ -3120,7 +3533,7 @@
 local s=e;
 e=i;
 if o.types then
-for o,a in f(o.types)do
+for o,a in m(o.types)do
 local a=a:lower();
 if(t.TYPE and t.TYPE[a]==true)
 or t[a]==true then
@@ -3129,12 +3542,12 @@
 end
 end
 if o.props then
-for o,a in f(o.props)do
+for o,a in m(o.props)do
 if t[a]then
 if t[a]==true then
 e[a]=true;
 else
-for o,t in f(t[a])do
+for o,t in m(t[a])do
 e[a]=t;
 end
 end
@@ -3154,123 +3567,123 @@
 end
 return h;
 end
-local function o(e)
+local function n(e)
 local t={};
 for a=1,#e do
-t[a]=y(e[a]);
+t[a]=w(e[a]);
 end
 t=u(t,";");
 local a="";
-for e,t in r(e)do
+for e,t in s(e)do
 if h(e)=="string"and e~="name"then
 a=a..(";%s=%s"):format(e,h(t)=="table"and u(t,",")or t);
 end
 end
 return("%s%s:%s"):format(e.name,a,t)
 end
-local function t(t)
+local function o(t)
 local e={};
 a(e,"BEGIN:VCARD")
-for i=1,#t do
-a(e,o(t[i]));
+for o=1,#t do
+a(e,n(t[o]));
 end
 a(e,"END:VCARD")
-return u(e,w);
-end
-function c(e)
+return u(e,f);
+end
+function d(e)
 if e[1]and e[1].name then
-return t(e)
+return o(e)
 else
 local a={};
-for o=1,#e do
-a[o]=t(e[o]);
-end
-return u(a,w);
+for t=1,#e do
+a[t]=o(e[t]);
+end
+return u(a,f);
 end
 end
 local function n(o)
-local e=o.name;
-local t=i[e];
-local e={name=e};
-if t=="text"then
-e[1]=o:get_text();
-elseif h(t)=="table"then
-if t.value then
-e[1]=o:get_child_text(t.value)or"";
-elseif t.values then
-local t=t.values;
-if t.behaviour=="repeat-last"then
-for t=1,#o.tags do
-a(e,o.tags[t]:get_text()or"");
-end
-else
-for i=1,#t do
-a(e,o:get_child_text(t[i])or"");
-end
-end
-elseif t.names then
-local t=t.names;
-for a=1,#t do
-if o:get_child(t[a])then
-e[1]=t[a];
+local t=o.name;
+local e=i[t];
+local t={name=t};
+if e=="text"then
+t[1]=o:get_text();
+elseif h(e)=="table"then
+if e.value then
+t[1]=o:get_child_text(e.value)or"";
+elseif e.values then
+local e=e.values;
+if e.behaviour=="repeat-last"then
+for e=1,#o.tags do
+a(t,o.tags[e]:get_text()or"");
+end
+else
+for i=1,#e do
+a(t,o:get_child_text(e[i])or"");
+end
+end
+elseif e.names then
+local e=e.names;
+for a=1,#e do
+if o:get_child(e[a])then
+t[1]=e[a];
 break;
 end
 end
 end
-if t.props_verbatim then
-for t,a in r(t.props_verbatim)do
-e[t]=a;
-end
-end
-if t.types then
-local t=t.types;
-e.TYPE={};
-for i=1,#t do
-if o:get_child(t[i])then
-a(e.TYPE,t[i]:lower());
-end
-end
-if#e.TYPE==0 then
-e.TYPE=nil;
-end
-end
-if t.props then
-local t=t.props;
-for i=1,#t do
-local t=t[i]
-local o=o:get_child_text(t);
+if e.props_verbatim then
+for e,a in s(e.props_verbatim)do
+t[e]=a;
+end
+end
+if e.types then
+local e=e.types;
+t.TYPE={};
+for i=1,#e do
+if o:get_child(e[i])then
+a(t.TYPE,e[i]:lower());
+end
+end
+if#t.TYPE==0 then
+t.TYPE=nil;
+end
+end
+if e.props then
+local e=e.props;
+for i=1,#e do
+local e=e[i]
+local o=o:get_child_text(e);
 if o then
-e[t]=e[t]or{};
-a(e[t],o);
+t[e]=t[e]or{};
+a(t[e],o);
 end
 end
 end
 else
 return nil
 end
-return e;
-end
-local function o(e)
-local t=e.tags;
-local e={};
-for o=1,#t do
-a(e,n(t[o]));
-end
-return e
-end
-function d(e)
+return t;
+end
+local function t(e)
+local e=e.tags;
+local t={};
+for o=1,#e do
+a(t,n(e[o]));
+end
+return t
+end
+function l(e)
 if e.attr.xmlns~="vcard-temp"then
 return nil,"wrong-xmlns";
 end
 if e.name=="xCard"then
-local t={};
+local a={};
 local e=e.tags;
-for a=1,#e do
-t[a]=o(e[a]);
-end
-return t
+for o=1,#e do
+a[o]=t(e[o]);
+end
+return a
 elseif e.name=="vCard"then
-return o(e)
+return t(e)
 end
 end
 i={
@@ -3398,138 +3811,909 @@
 i.LOGO=i.PHOTO;
 i.SOUND=i.PHOTO;
 return{
-from_text=m;
-to_text=c;
-from_xep54=d;
-to_xep54=l;
-lua_to_text=c;
-lua_to_xep54=l;
-text_to_lua=m;
-text_to_xep54=function(...)return l(m(...));end;
-xep54_to_lua=d;
-xep54_to_text=function(...)return c(d(...))end;
+from_text=c;
+to_text=d;
+from_xep54=l;
+to_xep54=r;
+lua_to_text=d;
+lua_to_xep54=r;
+text_to_lua=c;
+text_to_xep54=function(...)return r(c(...));end;
+xep54_to_lua=l;
+xep54_to_text=function(...)return d(l(...))end;
 };
 end)
 package.preload['util.logger']=(function(...)
-local e=pcall;
-local e=string.find;
-local e,n,e=ipairs,pairs,setmetatable;
-module"logger"
-local e,t={},{};
-local a={};
-local o;
-function init(e)
-local i=o(e,"debug");
-local a=o(e,"info");
-local n=o(e,"warn");
-local o=o(e,"error");
-local e=#e;
+local o=pairs;
+local e=nil;
+local t={};
+local e;
+local function i(t)
+local a=e(t,"debug");
+local n=e(t,"info");
+local i=e(t,"warn");
+local o=e(t,"error");
 return function(t,e,...)
 if t=="debug"then
-return i(e,...);
+return a(e,...);
 elseif t=="info"then
-return a(e,...);
+return n(e,...);
 elseif t=="warn"then
-return n(e,...);
+return i(e,...);
 elseif t=="error"then
 return o(e,...);
 end
 end
 end
-function o(i,o)
-local a=t[o];
-if not a then
-a={};
-t[o]=a;
-end
-local e=e[i];
-local e=function(t,...)
-if e then
-for a=1,#e do
-if e[a](i,o,t,...)==false then
-return;
-end
-end
-end
-for e=1,#a do
-a[e](i,o,t,...);
+function e(o,a)
+local e=t[a];
+if not e then
+e={};
+t[a]=e;
+end
+local e=function(i,...)
+for t=1,#e do
+e[t](o,a,i,...);
 end
 end
 return e;
 end
-function reset()
-for t in n(e)do e[t]=nil;end
-for t,e in n(t)do
+local function n()
+for t,e in o(t)do
 for t=1,#e do
 e[t]=nil;
 end
 end
-for e in n(a)do a[e]=nil;end
-end
-function add_level_sink(e,a)
+end
+local function o(e,a)
 if not t[e]then
 t[e]={a};
 else
 t[e][#t[e]+1]=a;
 end
 end
-function add_name_sink(t,a,o)
-if not e[t]then
-e[t]={a};
-else
-e[t][#e[t]+1]=a;
-end
-end
-function add_name_pattern_sink(e,t,o)
-if not a[e]then
-a[e]={t};
-else
-a[e][#a[e]+1]=t;
-end
-end
-_M.new=o;
-return _M;
+return{
+init=i;
+make_logger=e;
+reset=n;
+add_level_sink=o;
+new=e;
+};
 end)
 package.preload['util.datetime']=(function(...)
 local e=os.date;
 local i=os.time;
-local u=os.difftime;
-local t=error;
-local h=tonumber;
-module"datetime"
-function date(t)
+local w=os.difftime;
+local l=tonumber;
+local t=nil;
+local function c(t)
 return e("!%Y-%m-%d",t);
 end
-function datetime(t)
+local function u(t)
 return e("!%Y-%m-%dT%H:%M:%SZ",t);
 end
-function time(t)
+local function m(t)
 return e("!%H:%M:%S",t);
 end
-function legacy(t)
+local function f(t)
 return e("!%Y%m%dT%H:%M:%S",t);
 end
-function parse(o)
-if o then
-local n,s,r,d,l,t,a;
-n,s,r,d,l,t,a=o:match("^(%d%d%d%d)-?(%d%d)-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-].*)$");
+local function y(t)
+if t then
+local n,d,h,s,r,o,a;
+n,d,h,s,r,o,a=t:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-]?.*)$");
 if n then
-local u=u(i(e("*t")),i(e("!*t")));
-local o=0;
+local u=w(i(e("*t")),i(e("!*t")));
+local t=0;
 if a~=""and a~="Z"then
-local a,t,e=a:match("([+%-])(%d%d):?(%d*)");
-if not a then return;end
+local o,a,e=a:match("([+%-])(%d%d):?(%d*)");
+if not o then return;end
 if#e~=2 then e="0";end
-t,e=h(t),h(e);
-o=t*60*60+e*60;
-if a=="-"then o=-o;end
-end
-t=(t+u)-o;
-return i({year=n,month=s,day=r,hour=d,min=l,sec=t,isdst=false});
-end
-end
-end
-return _M;
+a,e=l(a),l(e);
+t=a*60*60+e*60;
+if o=="-"then t=-t;end
+end
+o=(o+u)-t;
+return i({year=n,month=d,day=h,hour=s,min=r,sec=o,isdst=false});
+end
+end
+end
+return{
+date=c;
+datetime=u;
+time=m;
+legacy=f;
+parse=y;
+};
+end)
+package.preload['util.json']=(function(...)
+local v=type;
+local t,p,y,j=table.insert,table.concat,table.remove,table.sort;
+local n=string.char;
+local q,l=tostring,tonumber;
+local c,s=pairs,ipairs;
+local i=next;
+local b,w=getmetatable,setmetatable;
+local m=print;
+local a,e=pcall(require,"util.array");
+local f=a and b(e())or{};
+local a={};
+local r=w({},{__tostring=function()return"null";end;});
+a.null=r;
+local d={
+["\""]="\\\"",["\\"]="\\\\",["\b"]="\\b",
+["\f"]="\\f",["\n"]="\\n",["\r"]="\\r",["\t"]="\\t"};
+local e={
+["\""]="\"",["\\"]="\\",["/"]="/",
+b="\b",f="\f",n="\n",r="\r",t="\t"};
+for t=0,31 do
+local e=n(t);
+if not d[e]then d[e]=("\\u%.4X"):format(t);end
+end
+local function x(e)
+if e<128 then return n(e);end
+local t=e%64;
+if e<2048 then
+local e=(e-t)/64;
+return n(128+64+e,128+t);
+end
+local a=e%4096;
+local o=(a-t)/64;
+local e=(e-a)/4096;
+return n(128+64+32+e,128+o,128+t);
+end
+local g={
+number=true,
+string=true,
+table=true,
+boolean=true
+};
+local z={
+__array=true;
+__hash=true;
+};
+local o,k,h,u;
+function u(a,e)
+t(e,"\""..(a:gsub(".",d)).."\"");
+end
+function h(a,e)
+t(e,"[");
+if i(a)then
+for i,a in s(a)do
+o(a,e);
+t(e,",");
+end
+y(e);
+end
+t(e,"]");
+end
+function k(l,e)
+local a={};
+local d={};
+local n={};
+for e,t in s(l)do
+a[e]=t;
+end
+for e,t in c(l)do
+local o,i=v(e),v(t);
+if g[i]or t==r then
+if o=="string"and not z[e]then
+n[e]=t;
+elseif(g[o]or e==r)and a[e]==nil then
+d[e]=t;
+end
+end
+end
+if i(d)~=nil or i(n)~=nil or i(a)==nil then
+t(e,"{");
+local r=#e;
+if e.ordered then
+local a={};
+for e in c(n)do
+t(a,e);
+end
+j(a);
+for i,a in s(a)do
+u(a,e);
+t(e,":");
+o(n[a],e);
+t(e,",");
+end
+else
+for a,i in c(n)do
+u(a,e);
+t(e,":");
+o(i,e);
+t(e,",");
+end
+end
+if i(d)~=nil then
+t(e,"\"__hash\":[");
+for i,a in c(d)do
+o(i,e);
+t(e,",");
+o(a,e);
+t(e,",");
+end
+y(e);
+t(e,"]");
+t(e,",");
+end
+if i(a)then
+t(e,"\"__array\":");
+h(a,e);
+t(e,",");
+end
+if r~=#e then y(e);end
+t(e,"}");
+else
+h(a,e);
+end
+end
+function o(e,a)
+local o=v(e);
+if e==r then
+t(a,"null");
+elseif o=="number"then
+t(a,q(e));
+elseif o=="string"then
+u(e,a);
+elseif o=="table"then
+local t=b(e);
+if t==f then
+h(e,a);
+else
+k(e,a);
+end
+elseif o=="boolean"then
+t(a,(e and"true"or"false"));
+else
+t(a,"null");
+end
+end
+function a.encode(t)
+local e={};
+o(t,e);
+return p(e);
+end
+function a.encode_ordered(t)
+local e={ordered=true};
+o(t,e);
+return p(e);
+end
+function a.encode_array(t)
+local e={};
+h(t,e);
+return p(e);
+end
+local function o(t,e)
+return t:find("[^ \t\r\n]",e)or e;
+end
+local function d(e)
+local a=e.__array;
+if a then
+e.__array=nil;
+for o,a in s(a)do
+t(e,a);
+end
+end
+local a=e.__hash;
+if a then
+e.__hash=nil;
+local t;
+for o,a in s(a)do
+if t~=nil then
+e[t]=a;t=nil;
+else
+t=a;
+end
+end
+end
+return e;
+end
+local i,h;
+local function u(t,e)
+local a={};
+while true do
+local n,s;
+e=o(t,e+1);
+if t:byte(e)~=34 then
+if t:byte(e)==125 then return a,e+1;end
+return nil,"key expected";
+end
+n,e=h(t,e);
+if n==nil then return nil,e;end
+e=o(t,e);
+if t:byte(e)~=58 then return nil,"colon expected";end
+s,e=i(t,e+1);
+if s==nil then return nil,e;end
+a[n]=s;
+e=o(t,e);
+local t=t:byte(e);
+if t==125 then return d(a),e+1;end
+if t~=44 then return nil,"object eof";end
+end
+end
+local function c(a,e)
+local s={};
+local h=e;
+while true do
+local n;
+n,e=i(a,e+1);
+if n==nil then
+if a:byte(h+1)==93 then return w(s,f),h+2;end
+return n,e;
+end
+t(s,n);
+e=o(a,e);
+local t=a:byte(e);
+if t==93 then return w(s,f),e+1;end
+if t~=44 then return nil,"array eof";end
+end
+end
+local s;
+local function e(e)
+local t,e=l(e:sub(3,6),16),l(e:sub(9,12),16);
+local e=t*1024+e-56613888;
+local o=e%64;
+e=(e-o)/64;
+local a=e%64;
+e=(e-a)/64;
+local t=e%64;
+e=(e-t)/64;
+return n(240+e,128+t,128+a,128+o);
+end
+local function n(e)
+e=e:match("%x%x%x%x",3);
+if e then
+return x(l(e,16));
+end
+s=true;
+end
+function h(a,e)
+e=e+1;
+local t=a:find("\"",e,true);
+if t then
+local e=a:sub(e,t-1);
+s=nil;
+e=e:gsub("\\u.?.?.?.?",n);
+if s then return nil,"invalid escape";end
+return e,t+1;
+end
+return nil,"string eof";
+end
+local function d(e,t)
+local e=e:match("[0-9%.%-eE%+]+",t);
+return l(e),t+#e;
+end
+local function l(t,e)
+local t,o,a=t:byte(e+1,e+3);
+if t==117 and o==108 and a==108 then
+return r,e+4;
+end
+return nil,"null parse failed";
+end
+local function s(t,e)
+local o,a,t=t:byte(e+1,e+3);
+if o==114 and a==117 and t==101 then
+return true,e+4;
+end
+return nil,"true parse failed";
+end
+local function n(t,e)
+local t,o,a,i=t:byte(e+1,e+4);
+if t==97 and o==108 and a==115 and i==101 then
+return false,e+5;
+end
+return nil,"false parse failed";
+end
+function i(a,e)
+e=o(a,e);
+local t=a:byte(e);
+if t==123 then
+return u(a,e);
+elseif t==91 then
+return c(a,e);
+elseif t==34 then
+return h(a,e);
+elseif t~=nil and t>=48 and t<=57 or t==45 then
+return d(a,e);
+elseif t==110 then
+return l(a,e);
+elseif t==116 then
+return s(a,e);
+elseif t==102 then
+return n(a,e);
+else
+return nil,"value expected";
+end
+end
+local t={
+["\\\""]="\\u0022";
+["\\\\"]="\\u005c";
+["\\/"]="\\u002f";
+["\\b"]="\\u0008";
+["\\f"]="\\u000C";
+["\\n"]="\\u000A";
+["\\r"]="\\u000D";
+["\\t"]="\\u0009";
+["\\u"]="\\u";
+};
+function a.decode(e)
+e=e:gsub("\\.",t)
+local t,a=i(e,1);
+if t==nil then return t,a;end
+if e:find("[^ \t\r\n]",a)then return nil,"garbage at eof";end
+return t;
+end
+function a.test(e)
+local e=a.encode(e);
+local t=a.decode(e);
+local t=a.encode(t);
+if e~=t then
+m("FAILED");
+m("encoded:",e);
+m("recoded:",t);
+else
+m(e);
+end
+return e==t;
+end
+return a;
+end)
+package.preload['util.xml']=(function(...)
+local t=require"util.stanza";
+local h=require"lxp";
+local e=nil;
+local e=(function()
+local n={
+["http://www.w3.org/XML/1998/namespace"]="xml";
+};
+local e="\1";
+local i="^([^"..e.."]*)"..e.."?(.*)$";
+return function(s)
+local o={};
+local a=t.stanza("root");
+function o:StartElement(t,e)
+local t,o=t:match(i);
+if o==""then
+t,o="",t;
+end
+if t~=""then
+e.xmlns=t;
+end
+for t=1,#e do
+local a=e[t];
+e[t]=nil;
+local t,o=a:match(i);
+if o~=""then
+t=n[t];
+if t then
+e[t..":"..o]=e[a];
+e[a]=nil;
+end
+end
+end
+a:tag(o,e);
+end
+function o:CharacterData(e)
+a:text(e);
+end
+function o:EndElement()
+a:up();
+end
+local t=h.new(o,"\1");
+local e,n,i,o=t:parse(s);
+if e then e,n,i,o=t:parse();end
+if e then
+return a.tags[1];
+else
+return e,n.." (line "..i..", col "..o..")";
+end
+end;
+end)();
+return{
+parse=e;
+};
+end)
+package.preload['util.rsm']=(function(...)
+local s=require"util.stanza".stanza;
+local a,i=tostring,tonumber;
+local h=type;
+local r=pairs;
+local n='http://jabber.org/protocol/rsm';
+local o={};
+do
+local e=o;
+local function t(e)
+return i((e:get_text()));
+end
+local function a(t)
+return t:get_text();
+end
+e.after=a;
+e.before=function(e)
+local e=e:get_text();
+return e==""or e;
+end;
+e.max=t;
+e.index=t;
+e.first=function(e)
+return{index=i(e.attr.index);e:get_text()};
+end;
+e.last=a;
+e.count=t;
+end
+local h=setmetatable({
+first=function(t,e)
+if h(e)=="table"then
+t:tag("first",{index=e.index}):text(e[1]):up();
+else
+t:tag("first"):text(a(e)):up();
+end
+end;
+before=function(e,t)
+if t==true then
+e:tag("before"):up();
+else
+e:tag("before"):text(a(t)):up();
+end
+end
+},{
+__index=function(t,e)
+return function(o,t)
+o:tag(e):text(a(t)):up();
+end
+end;
+});
+local function a(e)
+local i={};
+for a in e:childtags()do
+local e=a.name;
+local t=e and o[e];
+if t then
+i[e]=t(a);
+end
+end
+return i;
+end
+local function i(t)
+local e=s("set",{xmlns=n});
+for t,a in r(t)do
+if o[t]then
+h[t](e,a);
+end
+end
+return e;
+end
+local function t(e)
+local e=e:get_child("set",n);
+if e and#e.tags>0 then
+return a(e);
+end
+end
+return{parse=a,generate=i,get=t};
+end)
+package.preload['util.random']=(function(...)
+local e=io.open("/dev/urandom","r");
+if e then
+return{
+seed=function()end;
+bytes=function(t)return e:read(t);end
+};
+end
+local e=require"crypto"
+return e.rand;
+end)
+package.preload['util.ip']=(function(...)
+local o={};
+local i={__index=function(t,e)return(o[e])(t);end,
+__tostring=function(e)return e.addr;end,
+__eq=function(e,t)return e.addr==t.addr;end};
+local n={["0"]="0000",["1"]="0001",["2"]="0010",["3"]="0011",["4"]="0100",["5"]="0101",["6"]="0110",["7"]="0111",["8"]="1000",["9"]="1001",["A"]="1010",["B"]="1011",["C"]="1100",["D"]="1101",["E"]="1110",["F"]="1111"};
+local function e(e,t)
+if not t then
+local a=e:match("^%x+(.)");
+if a==":"or(not(a)and e:sub(1,1)==":")then
+t="IPv6"
+elseif a=="."then
+t="IPv4"
+end
+if not t then
+return nil,"invalid address";
+end
+elseif t~="IPv4"and t~="IPv6"then
+return nil,"invalid protocol";
+end
+local a;
+if t=="IPv6"and e:find('%',1,true)then
+e,a=e:match("^(.-)%%(.*)");
+end
+if t=="IPv6"and e:find('.',1,true)then
+local t;
+e,t=e:gsub(":(%d+)%.(%d+)%.(%d+)%.(%d+)$",function(e,a,t,o)
+return(":%04X:%04X"):format(e*256+a,t*256+o);
+end);
+if t~=1 then return nil,"invalid-address";end
+end
+return setmetatable({addr=e,proto=t,zone=a},i);
+end
+local function i(t)
+local e="";
+local a={};
+if t.proto=="IPv4"then
+t=t.toV4mapped;
+end
+t=(t.addr):upper();
+t:gsub("([^:]*):?",function(e)a[#a+1]=e end);
+if not t:match(":$")then a[#a]=nil;end
+for o,t in ipairs(a)do
+if t:len()==0 and o~=1 and o~=#a then
+for t=1,16*(9-#a)do
+e=e.."0";
+end
+else
+for t=1,4-t:len()do
+e=e.."0000";
+end
+for a=1,t:len()do
+e=e..n[t:sub(a,a)];
+end
+end
+end
+return e;
+end
+local function t(a,t)
+a,t=i(a),i(t);
+for e=1,128 do
+if a:sub(e,e)~=t:sub(e,e)then
+return e-1;
+end
+end
+return 128;
+end
+local function s(t)
+local e={};
+t:gsub("([^.]*).?",function(t)e[#e+1]=tonumber(t)end);
+if e[1]==127 then
+return 2;
+elseif e[1]==169 and e[2]==254 then
+return 2;
+else
+return 14;
+end
+end
+local function r(e)
+if e:match("^[0:]*1$")then
+return 2;
+elseif e:match("^[Ff][Ee][89ABab]")then
+return 2;
+elseif e:match("^[Ff][Ee][CcDdEeFf]")then
+return 5;
+elseif e:match("^[Ff][Ff]")then
+return tonumber("0x"..e:sub(4,4));
+else
+return 14;
+end
+end
+local function n(a)
+if t(a,e("::1","IPv6"))==128 then
+return 0;
+elseif t(a,e("2002::","IPv6"))>=16 then
+return 2;
+elseif t(a,e("2001::","IPv6"))>=32 then
+return 5;
+elseif t(a,e("fc00::","IPv6"))>=7 then
+return 13;
+elseif t(a,e("fec0::","IPv6"))>=10 then
+return 11;
+elseif t(a,e("3ffe::","IPv6"))>=16 then
+return 12;
+elseif t(a,e("::","IPv6"))>=96 then
+return 3;
+elseif t(a,e("::ffff:0:0","IPv6"))>=96 then
+return 4;
+else
+return 1;
+end
+end
+local function i(a)
+if t(a,e("::1","IPv6"))==128 then
+return 50;
+elseif t(a,e("2002::","IPv6"))>=16 then
+return 30;
+elseif t(a,e("2001::","IPv6"))>=32 then
+return 5;
+elseif t(a,e("fc00::","IPv6"))>=7 then
+return 3;
+elseif t(a,e("fec0::","IPv6"))>=10 then
+return 1;
+elseif t(a,e("3ffe::","IPv6"))>=16 then
+return 1;
+elseif t(a,e("::","IPv6"))>=96 then
+return 1;
+elseif t(a,e("::ffff:0:0","IPv6"))>=96 then
+return 35;
+else
+return 40;
+end
+end
+local function h(o)
+local a={};
+local t="::ffff:";
+o:gsub("([^.]*).?",function(e)a[#a+1]=tonumber(e)end);
+t=t..("%02x"):format(a[1]);
+t=t..("%02x"):format(a[2]);
+t=t..":"
+t=t..("%02x"):format(a[3]);
+t=t..("%02x"):format(a[4]);
+return e(t,"IPv6");
+end
+function o:toV4mapped()
+if self.proto~="IPv4"then return nil,"No IPv4 address"end
+local e=h(self.addr);
+self.toV4mapped=e;
+return e;
+end
+function o:label()
+local e;
+if self.proto=="IPv4"then
+e=n(self.toV4mapped);
+else
+e=n(self);
+end
+self.label=e;
+return e;
+end
+function o:precedence()
+local e;
+if self.proto=="IPv4"then
+e=i(self.toV4mapped);
+else
+e=i(self);
+end
+self.precedence=e;
+return e;
+end
+function o:scope()
+local e;
+if self.proto=="IPv4"then
+e=s(self.addr);
+else
+e=r(self.addr);
+end
+self.scope=e;
+return e;
+end
+function o:private()
+local t=self.scope~=14;
+if not t and self.proto=="IPv4"then
+local a=self.addr;
+local e={};
+a:gsub("([^.]*).?",function(t)e[#e+1]=tonumber(t)end);
+if e[1]==127 or e[1]==10 or(e[1]==192 and e[2]==168)
+or(e[1]==172 and(e[2]>=16 or e[2]<=32))then
+t=true;
+end
+end
+self.private=t;
+return t;
+end
+local function i(t)
+local o;
+local a=t:find("/",1,true);
+if a then
+o=tonumber(t:sub(a+1,-1));
+t=t:sub(1,a-1);
+end
+return e(t),o;
+end
+local function n(e,a,o)
+local e=t(e,a);
+if o and a.proto=="IPv4"then
+e=e-96;
+end
+return e>=(o or 128);
+end
+return{new_ip=e,
+commonPrefixLength=t,
+parse_cidr=i,
+match=n};
+end)
+package.preload['util.time']=(function(...)
+local e=require"socket".gettime;
+return{
+now=e;
+}
+end)
+package.preload['util.sasl.scram']=(function(...)
+local i,l=require"mime".b64,require"mime".unb64;
+local e=require"util.hashes";
+local a=require"bit";
+local r=require"util.random";
+local p=tonumber;
+local d,t=string.char,string.byte;
+local s=string.gsub;
+local h=a.bxor;
+local function n(e,o)
+return(s(e,"()(.)",function(e,a)
+return d(h(t(a),t(o,e)))
+end));
+end
+local y,e=e.sha1,e.hmac_sha1;
+local function f(o,t,i)
+local t=e(o,t.."\0\0\0\1");
+local a=t;
+for i=2,i do
+t=e(o,t);
+a=n(a,t);
+end
+return a;
+end
+local function w(e)
+return e;
+end
+local function t(e)
+return(s(e,"[,=]",{[","]="=2C",["="]="=3D"}));
+end
+local function u(o,a)
+local t="n="..t(o.username);
+local d=i(r.bytes(15));
+local r="r="..d;
+local h=t..","..r;
+local s="";
+local t=o.conn:ssl()and"y"or"n";
+if a=="SCRAM-SHA-1-PLUS"then
+s=o.conn:socket():getfinished();
+t="p=tls-unique";
+end
+local u=t..",,";
+local t=u..h;
+local t,m=coroutine.yield(t);
+if t~="challenge"then return false end
+local a,t,c=m:match("(r=[^,]+),s=([^,]*),i=(%d+)");
+local c=p(c);
+t=l(t);
+if not a or not t or not c then
+return false,"Could not parse server_first_message";
+elseif a:find(d,3,true)~=3 then
+return false,"nonce sent by server does not match our nonce";
+elseif a==r then
+return false,"server did not append s-nonce to nonce";
+end
+local s=u..s;
+local s="c="..i(s);
+local s=s..","..a;
+local o=f(w(o.password),t,c);
+local t=e(o,"Client Key");
+local r=y(t);
+local a=h..","..m..","..s;
+local h=e(r,a);
+local n=n(t,h);
+local t=e(o,"Server Key");
+local t=e(t,a);
+local e="p="..i(n);
+local e=s..","..e;
+local e,a=coroutine.yield(e);
+if e~="success"then return false,"success-expected"end
+local e=a:match("v=([^,]+)");
+if l(e)~=t then
+return false,"server signature did not match";
+end
+return true;
+end
+return function(e,t)
+if e.username and(e.password or(e.client_key or e.server_key))then
+if t=="SCRAM-SHA-1"then
+return u,99;
+elseif t=="SCRAM-SHA-1-PLUS"then
+local e=e.conn:ssl()and e.conn:socket();
+if e and e.getfinished then
+return u,100;
+end
+end
+end
+end
 end)
 package.preload['util.sasl.plain']=(function(...)
 return function(e,t)
@@ -3540,11 +4724,20 @@
 end
 end
 end)
+package.preload['util.sasl.anonymous']=(function(...)
+return function(t,e)
+if e=="ANONYMOUS"then
+return function()
+return coroutine.yield()=="success";
+end,0;
+end
+end
+end)
 package.preload['verse.plugins.tls']=(function(...)
 local a=require"verse";
 local t="urn:ietf:params:xml:ns:xmpp-tls";
 function a.plugins.tls(e)
-local function o(o)
+local function i(o)
 if e.authenticated then return;end
 if o:get_child("starttls",t)and e.conn.starttls then
 e:debug("Negotiating TLS...");
@@ -3556,10 +4749,10 @@
 e:debug("Server doesn't offer TLS :(");
 end
 end
-local function i(t)
+local function o(t)
 if t.name=="proceed"then
 e:debug("Server says proceed, handshake starting...");
-e.conn:starttls({mode="client",protocol="sslv23",options="no_sslv2"},true);
+e.conn:starttls(e.ssl or{mode="client",protocol="sslv23",options="no_sslv2",capath="/etc/ssl/certs"},true);
 end
 end
 local function a(t)
@@ -3569,39 +4762,40 @@
 e:reopen();
 end
 end
-e:hook("stream-features",o,400);
-e:hook("stream/"..t,i);
+e:hook("stream-features",i,400);
+e:hook("stream/"..t,o);
 e:hook("status",a,400);
 return true;
 end
 end)
 package.preload['verse.plugins.sasl']=(function(...)
-local s,r=require"mime".b64,require"mime".unb64;
-local o="urn:ietf:params:xml:ns:xmpp-sasl";
-function verse.plugins.sasl(e)
-local function h(t)
+local n=require"verse";
+local s,h=require"mime".b64,require"mime".unb64;
+local a="urn:ietf:params:xml:ns:xmpp-sasl";
+function n.plugins.sasl(e)
+local function r(t)
 if e.authenticated then return;end
 e:debug("Authenticating with SASL...");
-local t=t:get_child("mechanisms",o);
+local t=t:get_child("mechanisms",a);
 if not t then return end
-local a={};
+local o={};
 local i={};
 for t in t:childtags("mechanism")do
 t=t:get_text();
 e:debug("Server offers %s",t);
-if not a[t]then
+if not o[t]then
 local n=t:match("[^-]+");
-local s,o=pcall(require,"util.sasl."..n:lower());
+local s,a=pcall(require,"util.sasl."..n:lower());
 if s then
 e:debug("Loaded SASL %s module",n);
-a[t],i[t]=o(e,t);
-elseif not tostring(o):match("not found")then
-e:debug("Loading failed: %s",tostring(o));
+o[t],i[t]=a(e,t);
+elseif not tostring(a):match("not found")then
+e:debug("Loading failed: %s",tostring(a));
 end
 end
 end
 local t={};
-for e in pairs(a)do
+for e in pairs(o)do
 table.insert(t,e);
 end
 if not t[1]then
@@ -3609,19 +4803,19 @@
 e:close();
 return;
 end
-table.sort(t,function(e,t)return i[e]>i[t];end);
+table.sort(t,function(t,e)return i[t]>i[e];end);
 local t,i=t[1];
 e:debug("Selecting %s mechanism...",t);
-e.sasl_mechanism=coroutine.wrap(a[t]);
+e.sasl_mechanism=coroutine.wrap(o[t]);
 i=e:sasl_mechanism(t);
-local t=verse.stanza("auth",{xmlns=o,mechanism=t});
+local t=n.stanza("auth",{xmlns=a,mechanism=t});
 if i then
 t:text(s(i));
 end
 e:send(t);
 return true;
 end
-local function i(t)
+local function o(t)
 if t.name=="failure"then
 local a=t.tags[1];
 local t=t:get_child_text("text");
@@ -3629,9 +4823,9 @@
 e:close();
 return false;
 end
-local t,a=e.sasl_mechanism(t.name,r(t:get_text()));
+local t,o=e.sasl_mechanism(t.name,h(t:get_text()));
 if not t then
-e:event("authentication-failure",{condition=a});
+e:event("authentication-failure",{condition=o});
 e:close();
 return false;
 elseif t==true then
@@ -3639,12 +4833,12 @@
 e.authenticated=true
 e:reopen();
 else
-e:send(verse.stanza("response",{xmlns=o}):text(s(t)));
+e:send(n.stanza("response",{xmlns=a}):text(s(t)));
 end
 return true;
 end
-e:hook("stream-features",h,300);
-e:hook("stream/"..o,i);
+e:hook("stream-features",r,300);
+e:hook("stream/"..a,o);
 return true;
 end
 end)
@@ -3667,8 +4861,8 @@
 e:event("bind-success",{jid=t});
 elseif t.attr.type=="error"then
 local a=t:child_with_name("error");
-local a,t,o=t:get_error();
-e:event("bind-failure",{error=t,text=o,type=a});
+local o,a,t=t:get_error();
+e:event("bind-failure",{error=a,text=t,type=o});
 end
 end);
 end
@@ -3677,58 +4871,57 @@
 end
 end)
 package.preload['verse.plugins.session']=(function(...)
-local a=require"verse";
-local o="urn:ietf:params:xml:ns:xmpp-session";
-function a.plugins.session(e)
-local function n(t)
-local t=t:get_child("session",o);
-if t and not t:get_child("optional")then
-local function i(t)
+local t=require"verse";
+local a="urn:ietf:params:xml:ns:xmpp-session";
+function t.plugins.session(e)
+local function i(o)
+local o=o:get_child("session",a);
+if o and not o:get_child("optional")then
+local function o(o)
 e:debug("Establishing Session...");
-e:send_iq(a.iq({type="set"}):tag("session",{xmlns=o}),
+e:send_iq(t.iq({type="set"}):tag("session",{xmlns=a}),
 function(t)
 if t.attr.type=="result"then
 e:event("session-success");
 elseif t.attr.type=="error"then
-local a=t:child_with_name("error");
 local t,o,a=t:get_error();
 e:event("session-failure",{error=o,text=a,type=t});
 end
 end);
 return true;
 end
-e:hook("bind-success",i);
-end
-end
-e:hook("stream-features",n);
+e:hook("bind-success",o);
+end
+end
+e:hook("stream-features",i);
 return true;
 end
 end)
 package.preload['verse.plugins.legacy']=(function(...)
 local i=require"verse";
-local n=require"util.uuid".generate;
-local a="jabber:iq:auth";
+local s=require"util.uuid".generate;
+local o="jabber:iq:auth";
 function i.plugins.legacy(e)
-function handle_auth_form(t)
-local o=t:get_child("query",a);
-if t.attr.type~="result"or not o then
-local o,t,a=t:get_error();
-e:debug("warn","%s %s: %s",o,t,a);
+local function n(t)
+local a=t:get_child("query",o);
+if t.attr.type~="result"or not a then
+local o,a,t=t:get_error();
+e:debug("warn","%s %s: %s",o,a,t);
 end
 local t={
 username=e.username;
 password=e.password;
-resource=e.resource or n();
+resource=e.resource or s();
 digest=false,sequence=false,token=false;
 };
-local a=i.iq({to=e.host,type="set"})
-:tag("query",{xmlns=a});
-if#o>0 then
-for o in o:childtags()do
-local o=o.name;
-local i=t[o];
+local o=i.iq({to=e.host,type="set"})
+:tag("query",{xmlns=o});
+if#a>0 then
+for a in a:childtags()do
+local a=a.name;
+local i=t[a];
 if i then
-a:tag(o):text(t[o]):up();
+o:tag(a):text(t[a]):up();
 elseif i==nil then
 local t="feature-not-implemented";
 e:event("authentication-failure",{condition=t});
@@ -3738,11 +4931,11 @@
 else
 for t,e in pairs(t)do
 if e then
-a:tag(t):text(e):up();
-end
-end
-end
-e:send_iq(a,function(a)
+o:tag(t):text(e):up();
+end
+end
+end
+e:send_iq(o,function(a)
 if a.attr.type=="result"then
 e.resource=t.resource;
 e.jid=t.username.."@"..e.host.."/"..t.resource;
@@ -3754,26 +4947,26 @@
 end
 end);
 end
-function handle_opened(t)
+local function t(t)
 if not t.version then
 e:send_iq(i.iq({type="get"})
 :tag("query",{xmlns="jabber:iq:auth"})
 :tag("username"):text(e.username),
-handle_auth_form);
-end
-end
-e:hook("opened",handle_opened);
+n);
+end
+end
+e:hook("opened",t);
 end
 end)
 package.preload['verse.plugins.compression']=(function(...)
 local a=require"verse";
-local e=require"zlib";
-local t="http://jabber.org/features/compress"
+local i=require"zlib";
+local e="http://jabber.org/features/compress"
 local t="http://jabber.org/protocol/compress"
-local o="http://etherx.jabber.org/streams";
-local n=9;
-local function i(o)
-local i,e=pcall(e.deflate,n);
+local e="http://etherx.jabber.org/streams";
+local e=9;
+local function h(o)
+local i,e=pcall(i.deflate,e);
 if i==false then
 local t=a.stanza("failure",{xmlns=t}):tag("setup-failed");
 o:send(t);
@@ -3782,8 +4975,8 @@
 end
 return e
 end
-local function s(o)
-local i,e=pcall(e.inflate);
+local function d(o)
+local i,e=pcall(i.inflate);
 if i==false then
 local t=a.stanza("failure",{xmlns=t}):tag("setup-failed");
 o:send(t);
@@ -3792,7 +4985,7 @@
 end
 return e
 end
-local function n(e,o)
+local function l(e,o)
 function e:send(i)
 local i,o,n=pcall(o,tostring(i),'sync');
 if i==false then
@@ -3807,12 +5000,12 @@
 e.conn:write(o);
 end;
 end
-local function h(e,o)
+local function r(e,i)
 local s=e.data
-e.data=function(i,n)
+e.data=function(n,o)
 e:debug("Decompressing data...");
-local n,o,h=pcall(o,n);
-if n==false then
+local i,o,h=pcall(i,o);
+if i==false then
 e:close({
 condition="undefined-condition";
 text=o;
@@ -3821,11 +5014,11 @@
 stream:warn("%s",tostring(o));
 return;
 end
-return s(i,o);
+return s(n,o);
 end;
 end
 function a.plugins.compression(e)
-local function r(o)
+local function i(o)
 if not e.compressed then
 local o=o:child_with_name("compression");
 if o then
@@ -3844,57 +5037,57 @@
 local function o(t)
 if t.name=="compressed"then
 e:debug("Activating compression...")
-local t=i(e);
+local t=h(e);
 if not t then return end
-local a=s(e);
+local a=d(e);
 if not a then return end
-n(e,t);
-h(e,a);
+l(e,t);
+r(e,a);
 e.compressed=true;
 e:reopen();
 elseif t.name=="failure"then
 e:warn("Failed to establish compression");
 end
 end
-e:hook("stream-features",r,250);
+e:hook("stream-features",i,250);
 e:hook("stream/"..t,o);
 end
 end)
 package.preload['verse.plugins.smacks']=(function(...)
-local i=require"verse";
-local h=socket.gettime;
-local s="urn:xmpp:sm:2";
-function i.plugins.smacks(e)
+local s=require"verse";
+local h=require"socket".gettime;
+local n="urn:xmpp:sm:2";
+function s.plugins.smacks(e)
 local t={};
-local o=0;
+local a=0;
 local r=h();
-local a;
-local n=0;
-local function d(t)
+local o;
+local i=0;
+local function l(t)
 if t.attr.xmlns=="jabber:client"or not t.attr.xmlns then
-n=n+1;
-e:debug("Increasing handled stanzas to %d for %s",n,t:top_tag());
-end
-end
-function outgoing_stanza(o)
-if o.name and not o.attr.xmlns then
-t[#t+1]=tostring(o);
+i=i+1;
+e:debug("Increasing handled stanzas to %d for %s",i,t:top_tag());
+end
+end
+local function d(a)
+if a.name and not a.attr.xmlns then
+t[#t+1]=tostring(a);
 r=h();
-if not a then
-a=true;
+if not o then
+o=true;
 e:debug("Waiting to send ack request...");
-i.add_task(1,function()
+s.add_task(1,function()
 if#t==0 then
-a=false;
+o=false;
 return;
 end
-local o=h()-r;
-if o<1 and#t<10 then
-return 1-o;
+local a=h()-r;
+if a<1 and#t<10 then
+return 1-a;
 end
 e:debug("Time up, sending <r>...");
-a=false;
-e:send(i.stanza("r",{xmlns=s}));
+o=false;
+e:send(s.stanza("r",{xmlns=n}));
 end);
 end
 end
@@ -3905,7 +5098,7 @@
 if e.resumption_token then
 e:debug("smacks: have resumption token, reconnecting in 1s...");
 e.authenticated=nil;
-i.add_task(1,function()
+s.add_task(1,function()
 e:connect(e.connect_host or e.host,e.connect_port or 5222);
 end);
 return true;
@@ -3915,37 +5108,37 @@
 e.resumption_token=nil;
 e:unhook("disconnected",h);
 end
-local function l(a)
-if a.name=="r"then
-e:debug("Ack requested... acking %d handled stanzas",n);
-e:send(i.stanza("a",{xmlns=s,h=tostring(n)}));
-elseif a.name=="a"then
-local a=tonumber(a.attr.h);
-if a>o then
+local function u(o)
+if o.name=="r"then
+e:debug("Ack requested... acking %d handled stanzas",i);
+e:send(s.stanza("a",{xmlns=n,h=tostring(i)}));
+elseif o.name=="a"then
+local o=tonumber(o.attr.h);
+if o>a then
 local i=#t;
-for a=o+1,a do
+for a=a+1,o do
 table.remove(t,1);
 end
-e:debug("Received ack: New ack: "..a.." Last ack: "..o.." Unacked stanzas now: "..#t.." (was "..i..")");
-o=a;
-else
-e:warn("Received bad ack for "..a.." when last ack was "..o);
-end
-elseif a.name=="enabled"then
-if a.attr.id then
-e.resumption_token=a.attr.id;
+e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")");
+a=o;
+else
+e:warn("Received bad ack for "..o.." when last ack was "..a);
+end
+elseif o.name=="enabled"then
+if o.attr.id then
+e.resumption_token=o.attr.id;
 e:hook("closed",r,100);
 e:hook("disconnected",h,100);
 end
-elseif a.name=="resumed"then
-local a=tonumber(a.attr.h);
-if a>o then
+elseif o.name=="resumed"then
+local o=tonumber(o.attr.h);
+if o>a then
 local i=#t;
-for a=o+1,a do
+for a=a+1,o do
 table.remove(t,1);
 end
-e:debug("Received ack: New ack: "..a.." Last ack: "..o.." Unacked stanzas now: "..#t.." (was "..i..")");
-o=a;
+e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")");
+a=o;
 end
 for a=1,#t do
 e:send(t[a]);
@@ -3954,25 +5147,25 @@
 e:debug("Resumed successfully");
 e:event("resumed");
 else
-e:warn("Don't know how to handle "..s.."/"..a.name);
+e:warn("Don't know how to handle "..n.."/"..o.name);
 end
 end
 local function o()
 if not e.smacks then
 e:debug("smacks: sending enable");
-e:send(i.stanza("enable",{xmlns=s,resume="true"}));
+e:send(s.stanza("enable",{xmlns=n,resume="true"}));
 e.smacks=true;
-e:hook("stanza",d);
-e:hook("outgoing",outgoing_stanza);
+e:hook("stanza",l);
+e:hook("outgoing",d);
 end
 end
 local function a(t)
-if t:get_child("sm",s)then
+if t:get_child("sm",n)then
 e.stream_management_supported=true;
 if e.smacks and e.bound then
-e:debug("Resuming stream with %d handled stanzas",n);
-e:send(i.stanza("resume",{xmlns=s,
-h=n,previd=e.resumption_token}));
+e:debug("Resuming stream with %d handled stanzas",i);
+e:send(s.stanza("resume",{xmlns=n,
+h=i,previd=e.resumption_token}));
 return true;
 else
 e:hook("bind-success",o,1);
@@ -3980,7 +5173,7 @@
 end
 end
 e:hook("stream-features",a,250);
-e:hook("stream/"..s,l);
+e:hook("stream/"..n,u);
 end
 end)
 package.preload['verse.plugins.keepalive']=(function(...)
@@ -3996,27 +5189,27 @@
 package.preload['verse.plugins.disco']=(function(...)
 local a=require"verse";
 local r=require("mime").b64;
-local l=require("util.sha1").sha1;
-local n="http://jabber.org/protocol/caps";
+local h=require("util.hashes").sha1;
+local s="http://jabber.org/protocol/caps";
 local e="http://jabber.org/protocol/disco";
-local o=e.."#info";
-local i=e.."#items";
+local i=e.."#info";
+local o=e.."#items";
 function a.plugins.disco(e)
 e:add_plugin("presence");
-local s={
-__index=function(a,e)
-local t={identities={},features={}};
+local t={
+__index=function(t,e)
+local a={identities={},features={}};
 if e=="identities"or e=="features"then
-return a[false][e]
-end
-a[e]=t;
-return t;
+return t[false][e]
+end
+t[e]=a;
+return a;
 end,
 };
-local t={
-__index=function(a,t)
+local n={
+__index=function(t,a)
 local e={};
-a[t]=e;
+t[a]=e;
 return e;
 end,
 };
@@ -4028,17 +5221,17 @@
 {category='client',type='pc',name='Verse'},
 },
 features={
-[n]=true,
+[s]=true,
+[i]=true,
 [o]=true,
-[i]=true,
 },
 },
-},s);
-items=setmetatable({[false]={}},t);
+},t);
+items=setmetatable({[false]={}},n);
 };
 e.caps={}
 e.caps.node='http://code.matthewwild.co.uk/verse/'
-local function d(t,e)
+local function n(t,e)
 if t.category<e.category then
 return true;
 elseif e.category<t.category then
@@ -4055,37 +5248,37 @@
 end
 return false
 end
-local function h(t,e)
-return t.var<e.var
-end
-local function s(a)
-local o=e.disco.info[a or false].identities;
-table.sort(o,d)
-local t={};
-for e in pairs(e.disco.info[a or false].features)do
-t[#t+1]={var=e};
-end
-table.sort(t,h)
+local function d(e,t)
+return e.var<t.var
+end
+local function l(o)
+local t=e.disco.info[o or false].identities;
+table.sort(t,n)
+local a={};
+for e in pairs(e.disco.info[o or false].features)do
+a[#a+1]={var=e};
+end
+table.sort(a,d)
 local e={};
-for a,t in pairs(o)do
+for a,t in pairs(t)do
 e[#e+1]=table.concat({
 t.category,t.type or'',
 t['xml:lang']or'',t.name or''
 },'/');
 end
-for a,t in pairs(t)do
+for a,t in pairs(a)do
 e[#e+1]=t.var
 end
 e[#e+1]='';
 e=table.concat(e,'<');
-return(r(l(e)))
+return(r(h(e)))
 end
 setmetatable(e.caps,{
 __call=function(...)
-local t=s()
+local t=l()
 e.caps.hash=t;
 return a.stanza('c',{
-xmlns=n,
+xmlns=s,
 hash='sha-1',
 node=e.caps.node,
 ver=t
@@ -4123,17 +5316,17 @@
 end
 end
 end
-function e:jid_has_identity(a,e,t)
-local o=self.disco.cache[a];
+function e:jid_has_identity(e,t,a)
+local o=self.disco.cache[e];
 if not o then
 return nil,"no-cache";
 end
-local a=self.disco.cache[a].identities;
-if t then
-return a[e.."/"..t]or false;
-end
-for t in pairs(a)do
-if t:match("^(.*)/")==e then
+local e=self.disco.cache[e].identities;
+if a then
+return e[t.."/"..a]or false;
+end
+for e in pairs(e)do
+if e:match("^(.*)/")==t then
 return true;
 end
 end
@@ -4183,15 +5376,15 @@
 end
 function e:disco_info(e,t,s)
 local a=a.iq({to=e,type="get"})
-:tag("query",{xmlns=o,node=t});
-self:send_iq(a,function(i)
-if i.attr.type=="error"then
-return s(nil,i:get_error());
-end
-local n,a={},{};
-for e in i:get_child("query",o):childtags()do
+:tag("query",{xmlns=i,node=t});
+self:send_iq(a,function(n)
+if n.attr.type=="error"then
+return s(nil,n:get_error());
+end
+local o,a={},{};
+for e in n:get_child("query",i):childtags()do
 if e.name=="identity"then
-n[e.attr.category.."/"..e.attr.type]=e.attr.name or true;
+o[e.attr.category.."/"..e.attr.type]=e.attr.name or true;
 elseif e.name=="feature"then
 a[e.attr.var]=true;
 end
@@ -4203,75 +5396,75 @@
 if not self.disco.cache[e].nodes[t]then
 self.disco.cache[e].nodes[t]={nodes={}};
 end
-self.disco.cache[e].nodes[t].identities=n;
+self.disco.cache[e].nodes[t].identities=o;
 self.disco.cache[e].nodes[t].features=a;
 else
-self.disco.cache[e].identities=n;
+self.disco.cache[e].identities=o;
 self.disco.cache[e].features=a;
 end
 return s(self.disco.cache[e]);
 end);
 end
-function e:disco_items(t,o,n)
+function e:disco_items(t,i,n)
 local a=a.iq({to=t,type="get"})
-:tag("query",{xmlns=i,node=o});
-self:send_iq(a,function(e)
-if e.attr.type=="error"then
-return n(nil,e:get_error());
-end
-local a={};
-for e in e:get_child("query",i):childtags()do
-if e.name=="item"then
-table.insert(a,{
-name=e.attr.name;
-jid=e.attr.jid;
-node=e.attr.node;
+:tag("query",{xmlns=o,node=i});
+self:send_iq(a,function(a)
+if a.attr.type=="error"then
+return n(nil,a:get_error());
+end
+local e={};
+for t in a:get_child("query",o):childtags()do
+if t.name=="item"then
+table.insert(e,{
+name=t.attr.name;
+jid=t.attr.jid;
+node=t.attr.node;
 });
 end
 end
 if not self.disco.cache[t]then
 self.disco.cache[t]={nodes={}};
 end
-if o then
-if not self.disco.cache[t].nodes[o]then
-self.disco.cache[t].nodes[o]={nodes={}};
-end
-self.disco.cache[t].nodes[o].items=a;
-else
-self.disco.cache[t].items=a;
-end
-return n(a);
+if i then
+if not self.disco.cache[t].nodes[i]then
+self.disco.cache[t].nodes[i]={nodes={}};
+end
+self.disco.cache[t].nodes[i].items=e;
+else
+self.disco.cache[t].items=e;
+end
+return n(e);
 end);
 end
-e:hook("iq/"..o,function(n)
-local t=n.tags[1];
-if n.attr.type=='get'and t.name=="query"then
+e:hook("iq/"..i,function(o)
+local t=o.tags[1];
+if o.attr.type=='get'and t.name=="query"then
 local t=t.attr.node;
-local i=e.disco.info[t or false];
+local n=e.disco.info[t or false];
 if t and t==e.caps.node.."#"..e.caps.hash then
-i=e.disco.info[false];
-end
-local s,i=i.identities,i.features
-local t=a.reply(n):tag("query",{
-xmlns=o,
+n=e.disco.info[false];
+end
+local n,s=n.identities,n.features
+local t=a.reply(o):tag("query",{
+xmlns=i,
 node=t,
 });
-for a,e in pairs(s)do
+for a,e in pairs(n)do
 t:tag('identity',e):up()
 end
-for a in pairs(i)do
+for a in pairs(s)do
 t:tag('feature',{var=a}):up()
 end
 e:send(t);
 return true
 end
 end);
-e:hook("iq/"..i,function(o)
-local t=o.tags[1];
-if o.attr.type=='get'and t.name=="query"then
+e:hook("iq/"..o,function(i)
+local t=i.tags[1];
+if i.attr.type=='get'and t.name=="query"then
 local n=e.disco.items[t.attr.node or false];
-local t=a.reply(o):tag('query',{
-xmlns=i,
+local t=a.reply(i):tag('query',{
+xmlns=o,
 node=t.attr.node
 })
 for a=1,#n do
@@ -4286,13 +5479,13 @@
 if t then return;end
 t=true;
 e:disco_local_services(function(t)
-for a,t in ipairs(t)do
-local a=e.disco.cache[t.jid];
-if a then
-for a in pairs(a.identities)do
-local a,o=a:match("^(.*)/(.*)$");
-e:event("disco/service-discovered/"..a,{
-type=o,jid=t.jid;
+for t,a in ipairs(t)do
+local t=e.disco.cache[a.jid];
+if t then
+for t in pairs(t.identities)do
+local o,t=t:match("^(.*)/(.*)$");
+e:event("disco/service-discovered/"..o,{
+type=t,jid=a.jid;
 });
 end
 end
@@ -4302,7 +5495,7 @@
 return true;
 end,50);
 e:hook("presence-out",function(t)
-if not t:get_child("c",n)then
+if not t:get_child("c",s)then
 t:reset():add_child(e:caps()):reset();
 end
 end,10);
@@ -4334,9 +5527,9 @@
 e:send(t);
 return true;
 end);
-function e:query_version(i,t)
-t=t or function(t)return e:event("version/response",t);end
-e:send_iq(o.iq({type="get",to=i})
+function e:query_version(e,t)
+t=t or function(e)return self:event("version/response",e);end
+self:send_iq(o.iq({type="get",to=e})
 :tag("query",{xmlns=a}),
 function(o)
 if o.attr.type=="result"then
@@ -4350,12 +5543,12 @@
 platform=e;
 });
 else
-local a,e,o=o:get_error();
+local e,o,a=o:get_error();
 t({
 error=true;
-condition=e;
-text=o;
-type=a;
+condition=o;
+text=a;
+type=e;
 });
 end
 end);
@@ -4364,62 +5557,63 @@
 end
 end)
 package.preload['verse.plugins.ping']=(function(...)
-local a=require"verse";
-local o="urn:xmpp:ping";
-function a.plugins.ping(e)
-function e:ping(t,i)
-local n=socket.gettime();
-e:send_iq(a.iq{to=t,type="get"}:tag("ping",{xmlns=o}),
+local t=require"verse";
+local i=require"socket".gettime;
+local n="urn:xmpp:ping";
+function t.plugins.ping(e)
+function e:ping(a,o)
+local s=i();
+e:send_iq(t.iq{to=a,type="get"}:tag("ping",{xmlns=n}),
 function(e)
 if e.attr.type=="error"then
-local a,e,o=e:get_error();
+local t,e,i=e:get_error();
 if e~="service-unavailable"and e~="feature-not-implemented"then
-i(nil,t,{type=a,condition=e,text=o});
+o(nil,a,{type=t,condition=e,text=i});
 return;
 end
 end
-i(socket.gettime()-n,t);
+o(i()-s,a);
 end);
 end
-e:hook("iq/"..o,function(t)
-return e:send(a.reply(t));
+e:hook("iq/"..n,function(a)
+return e:send(t.reply(a));
 end);
 return true;
 end
 end)
 package.preload['verse.plugins.uptime']=(function(...)
 local o=require"verse";
-local a="jabber:iq:last";
-local function t(e,t)
-e.starttime=t.starttime;
+local t="jabber:iq:last";
+local function a(t,e)
+t.starttime=e.starttime;
 end
 function o.plugins.uptime(e)
-e.uptime={set=t};
-e:hook("iq/"..a,function(t)
-if t.attr.type~="get"then return;end
-local t=o.reply(t)
-:tag("query",{seconds=tostring(os.difftime(os.time(),e.uptime.starttime)),xmlns=a});
+e.uptime={set=a};
+e:hook("iq/"..t,function(a)
+if a.attr.type~="get"then return;end
+local t=o.reply(a)
+:tag("query",{seconds=tostring(os.difftime(os.time(),e.uptime.starttime)),xmlns=t});
 e:send(t);
 return true;
 end);
-function e:query_uptime(i,t)
-t=t or function(t)return e:event("uptime/response",t);end
+function e:query_uptime(i,a)
+a=a or function(t)return e:event("uptime/response",t);end
 e:send_iq(o.iq({type="get",to=i})
-:tag("query",{xmlns=a}),
+:tag("query",{xmlns=t}),
 function(e)
-local a=e:get_child("query",a);
+local t=e:get_child("query",t);
 if e.attr.type=="result"then
-local e=tonumber(a.attr.seconds);
-t({
+local e=tonumber(t.attr.seconds);
+a({
 seconds=e or nil;
 });
 else
-local o,a,e=e:get_error();
-t({
+local e,o,t=e:get_error();
+a({
 error=true;
-condition=a;
-text=e;
-type=o;
+condition=o;
+text=t;
+type=e;
 });
 end
 end);
@@ -4428,38 +5622,38 @@
 end
 end)
 package.preload['verse.plugins.blocking']=(function(...)
-local o=require"verse";
-local a="urn:xmpp:blocking";
-function o.plugins.blocking(e)
+local a=require"verse";
+local o="urn:xmpp:blocking";
+function a.plugins.blocking(e)
 e.blocking={};
 function e.blocking:block_jid(i,t)
-e:send_iq(o.iq{type="set"}
-:tag("block",{xmlns=a})
+e:send_iq(a.iq{type="set"}
+:tag("block",{xmlns=o})
 :tag("item",{jid=i})
 ,function()return t and t(true);end
 ,function()return t and t(false);end
 );
 end
 function e.blocking:unblock_jid(i,t)
-e:send_iq(o.iq{type="set"}
-:tag("unblock",{xmlns=a})
+e:send_iq(a.iq{type="set"}
+:tag("unblock",{xmlns=o})
 :tag("item",{jid=i})
 ,function()return t and t(true);end
 ,function()return t and t(false);end
 );
 end
 function e.blocking:unblock_all_jids(t)
-e:send_iq(o.iq{type="set"}
-:tag("unblock",{xmlns=a})
+e:send_iq(a.iq{type="set"}
+:tag("unblock",{xmlns=o})
 ,function()return t and t(true);end
 ,function()return t and t(false);end
 );
 end
 function e.blocking:get_blocked_jids(t)
-e:send_iq(o.iq{type="get"}
-:tag("blocklist",{xmlns=a})
+e:send_iq(a.iq{type="get"}
+:tag("blocklist",{xmlns=o})
 ,function(e)
-local a=e:get_child("blocklist",a);
+local a=e:get_child("blocklist",o);
 if not a then return t and t(false);end
 local e={};
 for t in a:childtags()do
@@ -4473,25 +5667,24 @@
 end
 end)
 package.preload['verse.plugins.jingle']=(function(...)
-local o=require"verse";
-local e=require"util.sha1".sha1;
+local a=require"verse";
 local e=require"util.timer";
-local a=require"util.uuid".generate;
+local o=require"util.uuid".generate;
 local i="urn:xmpp:jingle:1";
-local s="urn:xmpp:jingle:errors:1";
+local h="urn:xmpp:jingle:errors:1";
 local t={};
 t.__index=t;
 local e={};
 local e={};
-function o.plugins.jingle(e)
+function a.plugins.jingle(e)
 e:hook("ready",function()
 e:add_disco_feature(i);
 end,10);
 function e:jingle(i)
-return o.eventable(setmetatable(base or{
+return a.eventable(setmetatable(base or{
 role="initiator";
 peer=i;
-sid=a();
+sid=o();
 stream=e;
 },t));
 end
@@ -4500,71 +5693,71 @@
 function e:register_jingle_content_type(e)
 end
 local function u(n)
-local h=n:get_child("jingle",i);
-local r=h.attr.sid;
-local a=h.attr.action;
-local r=e:event("jingle/"..r,n);
-if r==true then
-e:send(o.reply(n));
+local r=n:get_child("jingle",i);
+local s=r.attr.sid;
+local o=r.attr.action;
+local s=e:event("jingle/"..s,n);
+if s==true then
+e:send(a.reply(n));
 return true;
 end
-if a~="session-initiate"then
-local t=o.error_reply(n,"cancel","item-not-found")
-:tag("unknown-session",{xmlns=s}):up();
+if o~="session-initiate"then
+local t=a.error_reply(n,"cancel","item-not-found")
+:tag("unknown-session",{xmlns=h}):up();
 e:send(t);
 return;
 end
-local l=h.attr.sid;
-local a=o.eventable{
+local l=r.attr.sid;
+local o=a.eventable{
 role="receiver";
 peer=n.attr.from;
 sid=l;
 stream=e;
 };
-setmetatable(a,t);
-local d;
-local r,s;
-for t in h:childtags()do
+setmetatable(o,t);
+local s;
+local h,d;
+for t in r:childtags()do
 if t.name=="content"and t.attr.xmlns==i then
 local i=t:child_with_name("description");
-local o=i.attr.xmlns;
-if o then
-local e=e:event("jingle/content/"..o,a,i);
+local a=i.attr.xmlns;
+if a then
+local e=e:event("jingle/content/"..a,o,i);
 if e then
-r=e;
-end
-end
-local o=t:child_with_name("transport");
-local i=o.attr.xmlns;
-s=e:event("jingle/transport/"..i,a,o);
-if r and s then
-d=t;
+h=e;
+end
+end
+local a=t:child_with_name("transport");
+local i=a.attr.xmlns;
+d=e:event("jingle/transport/"..i,o,a);
+if h and d then
+s=t;
 break;
 end
 end
 end
-if not r then
-e:send(o.error_reply(n,"cancel","feature-not-implemented","The specified content is not supported"));
+if not h then
+e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified content is not supported"));
 return true;
 end
-if not s then
-e:send(o.error_reply(n,"cancel","feature-not-implemented","The specified transport is not supported"));
+if not d then
+e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified transport is not supported"));
 return true;
 end
-e:send(o.reply(n));
-a.content_tag=d;
-a.creator,a.name=d.attr.creator,d.attr.name;
-a.content,a.transport=r,s;
-function a:decline()
+e:send(a.reply(n));
+o.content_tag=s;
+o.creator,o.name=s.attr.creator,s.attr.name;
+o.content,o.transport=h,d;
+function o:decline()
 end
 e:hook("jingle/"..l,function(e)
-if e.attr.from~=a.peer then
+if e.attr.from~=o.peer then
 return false;
 end
 local e=e:get_child("jingle",i);
-return a:handle_command(e);
+return o:handle_command(e);
 end);
-e:event("jingle",a);
+e:event("jingle",o);
 return true;
 end
 function t:handle_command(a)
@@ -4585,23 +5778,23 @@
 end
 return true;
 end
-function t:send_command(a,e,t)
-local e=o.iq({to=self.peer,type="set"})
+function t:send_command(o,t,e)
+local t=a.iq({to=self.peer,type="set"})
 :tag("jingle",{
 xmlns=i,
 sid=self.sid,
-action=a,
+action=o,
 initiator=self.role=="initiator"and self.stream.jid or nil,
 responder=self.role=="responder"and self.jid or nil,
-}):add_child(e);
-if not t then
-self.stream:send(e);
-else
-self.stream:send_iq(e,t);
-end
-end
-function t:accept(t)
-local a=o.iq({to=self.peer,type="set"})
+}):add_child(t);
+if not e then
+self.stream:send(t);
+else
+self.stream:send_iq(t,e);
+end
+end
+function t:accept(o)
+local t=a.iq({to=self.peer,type="set"})
 :tag("jingle",{
 xmlns=i,
 sid=self.sid,
@@ -4609,33 +5802,33 @@
 responder=e.jid,
 })
 :tag("content",{creator=self.creator,name=self.name});
-local o=self.content:generate_accept(self.content_tag:child_with_name("description"),t);
-a:add_child(o);
-local t=self.transport:generate_accept(self.content_tag:child_with_name("transport"),t);
-a:add_child(t);
-local t=self;
-e:send_iq(a,function(a)
-if a.attr.type=="error"then
-local a,t,a=a:get_error();
+local a=self.content:generate_accept(self.content_tag:child_with_name("description"),o);
+t:add_child(a);
+local a=self.transport:generate_accept(self.content_tag:child_with_name("transport"),o);
+t:add_child(a);
+local a=self;
+e:send_iq(t,function(t)
+if t.attr.type=="error"then
+local a,t,a=t:get_error();
 e:error("session-accept rejected: %s",t);
 return false;
 end
-t.transport:connect(function(a)
+a.transport:connect(function(t)
 e:warn("CONNECTED (receiver)!!!");
-t.state="active";
-t:event("connected",a);
+a.state="active";
+a:event("connected",t);
 end);
 end);
 end
 e:hook("iq/"..i,u);
 return true;
 end
-function t:offer(t,a)
-local e=o.iq({to=self.peer,type="set"})
+function t:offer(t,o)
+local e=a.iq({to=self.peer,type="set"})
 :tag("jingle",{xmlns=i,action="session-initiate",
 initiator=self.stream.jid,sid=self.sid});
 e:tag("content",{creator=self.role,name=t});
-local t=self.stream:event("jingle/describe/"..t,a);
+local t=self.stream:event("jingle/describe/"..t,o);
 if not t then
 return false,"Unknown content type";
 end
@@ -4654,14 +5847,14 @@
 self.stream:send_iq(e,function(e)
 if e.attr.type=="error"then
 self.state="terminated";
-local t,e,a=e:get_error();
-return self:event("error",{type=t,condition=e,text=a});
+local e,a,t=e:get_error();
+return self:event("error",{type=e,condition=a,text=t});
 end
 end);
 self.state="pending";
 end
 function t:terminate(e)
-local e=o.stanza("reason"):tag(e or"success");
+local e=a.stanza("reason"):tag(e or"success");
 self:send_command("session-terminate",e,function(e)
 self.state="terminated";
 self.transport:disconnect();
@@ -4710,59 +5903,58 @@
 end
 end)
 package.preload['verse.plugins.jingle_ft']=(function(...)
-local n=require"verse";
-local o=require"ltn12";
-local h=package.config:sub(1,1);
-local a="urn:xmpp:jingle:apps:file-transfer:1";
-local i="http://jabber.org/protocol/si/profile/file-transfer";
-function n.plugins.jingle_ft(t)
-t:hook("ready",function()
-t:add_disco_feature(a);
+local o=require"verse";
+local i=require"ltn12";
+local s=package.config:sub(1,1);
+local t="urn:xmpp:jingle:apps:file-transfer:4";
+function o.plugins.jingle_ft(e)
+e:hook("ready",function()
+e:add_disco_feature(t);
 end,10);
-local s={type="file"};
-function s:generate_accept(t,e)
+local a={type="file"};
+function a:generate_accept(t,e)
 if e and e.save_file then
 self.jingle:hook("connected",function()
-local e=o.sink.file(io.open(e.save_file,"w+"));
+local e=i.sink.file(io.open(e.save_file,"w+"));
 self.jingle:set_sink(e);
 end);
 end
 return t;
 end
-local s={__index=s};
-t:hook("jingle/content/"..a,function(t,e)
-local e=e:get_child("offer"):get_child("file",i);
+local a={__index=a};
+e:hook("jingle/content/"..t,function(t,e)
+local e=e:get_child("file");
 local e={
-name=e.attr.name;
-size=tonumber(e.attr.size);
+name=e:get_child_text("name");
+size=tonumber(e:get_child_text("size"));
+desc=e:get_child_text("desc");
+date=e:get_child_text("date");
 };
-return setmetatable({jingle=t,file=e},s);
+return setmetatable({jingle=t,file=e},a);
 end);
-t:hook("jingle/describe/file",function(e)
-local t;
+e:hook("jingle/describe/file",function(e)
+local a;
 if e.timestamp then
-t=os.date("!%Y-%m-%dT%H:%M:%SZ",e.timestamp);
-end
-return n.stanza("description",{xmlns=a})
-:tag("offer")
-:tag("file",{xmlns=i,
-name=e.filename,
-size=e.size,
-date=t,
-hash=e.hash,
-})
-:tag("desc"):text(e.description or"");
+a=os.date("!%Y-%m-%dT%H:%M:%SZ",e.timestamp);
+end
+return o.stanza("description",{xmlns=t})
+:tag("file")
+:tag("name"):text(e.filename):up()
+:tag("size"):text(tostring(e.size)):up()
+:tag("date"):text(a):up()
+:tag("desc"):text(e.description):up()
+:up();
 end);
-function t:send_file(n,t)
+function e:send_file(n,t)
 local e,a=io.open(t);
 if not e then return e,a;end
-local i=e:seek("end",0);
+local o=e:seek("end",0);
 e:seek("set",0);
-local a=o.source.file(e);
+local a=i.source.file(e);
 local e=self:jingle(n);
 e:offer("file",{
-filename=t:match("[^"..h.."]+$");
-size=i;
+filename=t:match("[^"..s.."]+$");
+size=o;
 });
 e:hook("connected",function()
 e:set_source(a,true);
@@ -4772,12 +5964,12 @@
 end
 end)
 package.preload['verse.plugins.jingle_s5b']=(function(...)
-local t=require"verse";
+local a=require"verse";
 local o="urn:xmpp:jingle:transports:s5b:1";
-local d="http://jabber.org/protocol/bytestreams";
-local s=require"util.sha1".sha1;
-local n=require"util.uuid".generate;
-local function r(e,t)
+local r="http://jabber.org/protocol/bytestreams";
+local n=require"util.hashes".sha1;
+local s=require"util.uuid".generate;
+local function d(e,t)
 local function a()
 e:unhook("connected",a);
 return true;
@@ -4807,70 +5999,70 @@
 e:hook("incoming-raw",i,100);
 e:send("\005\001\000");
 end
-local function h(a,e,i)
-local e=t.new(nil,{
+local function h(o,e,i)
+local e=a.new(nil,{
 streamhosts=e,
 current_host=0;
 });
-local function t(o)
-if o then
-return a(nil,o.reason);
+local function t(a)
+if a then
+return o(nil,a.reason);
 end
 if e.current_host<#e.streamhosts then
 e.current_host=e.current_host+1;
 e:debug("Attempting to connect to "..e.streamhosts[e.current_host].host..":"..e.streamhosts[e.current_host].port.."...");
-local a,t=e:connect(
+local t,a=e:connect(
 e.streamhosts[e.current_host].host,
 e.streamhosts[e.current_host].port
 );
-if not a then
+if not t then
 e:debug("Error connecting to proxy (%s:%s): %s",
 e.streamhosts[e.current_host].host,
 e.streamhosts[e.current_host].port,
-t
+a
 );
 else
 e:debug("Connecting...");
 end
-r(e,i);
+d(e,i);
 return true;
 end
 e:unhook("disconnected",t);
-return a(nil);
+return o(nil);
 end
 e:hook("disconnected",t,100);
 e:hook("connected",function()
 e:unhook("disconnected",t);
-a(e.streamhosts[e.current_host],e);
+o(e.streamhosts[e.current_host],e);
 end,100);
 t();
 return e;
 end
-function t.plugins.jingle_s5b(e)
+function a.plugins.jingle_s5b(e)
 e:hook("ready",function()
 e:add_disco_feature(o);
 end,10);
-local a={};
-function a:generate_initiate()
-self.s5b_sid=n();
-local i=t.stanza("transport",{xmlns=o,
+local t={};
+function t:generate_initiate()
+self.s5b_sid=s();
+local a=a.stanza("transport",{xmlns=o,
 mode="tcp",sid=self.s5b_sid});
 local t=0;
-for a,o in pairs(e.proxy65.available_streamhosts)do
+for o,i in pairs(e.proxy65.available_streamhosts)do
 t=t+1;
-i:tag("candidate",{jid=a,host=o.host,
-port=o.port,cid=a,priority=t,type="proxy"}):up();
+a:tag("candidate",{jid=o,host=i.host,
+port=i.port,cid=o,priority=t,type="proxy"}):up();
 end
 e:debug("Have %d proxies",t)
-return i;
-end
-function a:generate_accept(e)
-local a={};
-self.s5b_peer_candidates=a;
+return a;
+end
+function t:generate_accept(e)
+local t={};
+self.s5b_peer_candidates=t;
 self.s5b_mode=e.attr.mode or"tcp";
 self.s5b_sid=e.attr.sid or self.jingle.sid;
 for e in e:childtags()do
-a[e.attr.cid]={
+t[e.attr.cid]={
 type=e.attr.type;
 jid=e.attr.jid;
 host=e.attr.host;
@@ -4879,47 +6071,47 @@
 cid=e.attr.cid;
 };
 end
-local e=t.stanza("transport",{xmlns=o});
+local e=a.stanza("transport",{xmlns=o});
 return e;
 end
-function a:connect(i)
+function t:connect(i)
 e:warn("Connecting!");
-local a={};
-for t,e in pairs(self.s5b_peer_candidates or{})do
-a[#a+1]=e;
-end
-if#a>0 then
+local t={};
+for a,e in pairs(self.s5b_peer_candidates or{})do
+t[#t+1]=e;
+end
+if#t>0 then
 self.connecting_peer_candidates=true;
-local function n(e,a)
-self.jingle:send_command("transport-info",t.stanza("content",{creator=self.creator,name=self.name})
+local function s(t,e)
+self.jingle:send_command("transport-info",a.stanza("content",{creator=self.creator,name=self.name})
 :tag("transport",{xmlns=o,sid=self.s5b_sid})
-:tag("candidate-used",{cid=e.cid}));
+:tag("candidate-used",{cid=t.cid}));
 self.onconnect_callback=i;
-self.conn=a;
-end
-local e=s(self.s5b_sid..self.peer..e.jid,true);
-h(n,a,e);
+self.conn=e;
+end
+local e=n(self.s5b_sid..self.peer..e.jid,true);
+h(s,t,e);
 else
 e:warn("Actually, I'm going to wait for my peer to tell me its streamhost...");
 self.onconnect_callback=i;
 end
 end
-function a:info_received(a)
+function t:info_received(t)
 e:warn("Info received");
-local n=a:child_with_name("content");
-local i=n:child_with_name("transport");
+local s=t:child_with_name("content");
+local i=s:child_with_name("transport");
 if i:get_child("candidate-used")and not self.connecting_peer_candidates then
-local a=i:child_with_name("candidate-used");
-if a then
-local function r(i,e)
+local t=i:child_with_name("candidate-used");
+if t then
+local function d(i,e)
 if self.jingle.role=="initiator"then
-self.jingle.stream:send_iq(t.iq({to=i.jid,type="set"})
-:tag("query",{xmlns=d,sid=self.s5b_sid})
+self.jingle.stream:send_iq(a.iq({to=i.jid,type="set"})
+:tag("query",{xmlns=r,sid=self.s5b_sid})
 :tag("activate"):text(self.jingle.peer),function(i)
 if i.attr.type=="result"then
-self.jingle:send_command("transport-info",t.stanza("content",n.attr)
+self.jingle:send_command("transport-info",a.stanza("content",s.attr)
 :tag("transport",{xmlns=o,sid=self.s5b_sid})
-:tag("activated",{cid=a.attr.cid}));
+:tag("activated",{cid=t.attr.cid}));
 self.conn=e;
 self.onconnect_callback(e);
 else
@@ -4928,25 +6120,25 @@
 end);
 end
 end
-self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]);
+self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[t.attr.cid]);
 local t={
-self.jingle.stream.proxy65.available_streamhosts[a.attr.cid];
+self.jingle.stream.proxy65.available_streamhosts[t.attr.cid];
 };
-local e=s(self.s5b_sid..e.jid..self.peer,true);
-h(r,t,e);
+local e=n(self.s5b_sid..e.jid..self.peer,true);
+h(d,t,e);
 end
 elseif i:get_child("activated")then
 self.onconnect_callback(self.conn);
 end
 end
-function a:disconnect()
+function t:disconnect()
 if self.conn then
 self.conn:close();
 end
 end
-function a:handle_accepted(e)
-end
-local t={__index=a};
+function t:handle_accepted(e)
+end
+local t={__index=t};
 e:hook("jingle/transport/"..o,function(e)
 return setmetatable({
 role=e.role,
@@ -4958,25 +6150,25 @@
 end
 end)
 package.preload['verse.plugins.proxy65']=(function(...)
-local e=require"util.events";
-local r=require"util.uuid";
-local h=require"util.sha1";
-local i={};
-i.__index=i;
-local o="http://jabber.org/protocol/bytestreams";
-local n;
-function verse.plugins.proxy65(t)
-t.proxy65=setmetatable({stream=t},i);
+local a=require"verse";
+local h=require"util.uuid";
+local r=require"util.hashes".sha1;
+local n={};
+n.__index=n;
+local i="http://jabber.org/protocol/bytestreams";
+local s;
+function a.plugins.proxy65(t)
+t.proxy65=setmetatable({stream=t},n);
 t.proxy65.available_streamhosts={};
 local e=0;
-t:hook("disco/service-discovered/proxy",function(a)
-if a.type=="bytestreams"then
+t:hook("disco/service-discovered/proxy",function(o)
+if o.type=="bytestreams"then
 e=e+1;
-t:send_iq(verse.iq({to=a.jid,type="get"})
-:tag("query",{xmlns=o}),function(a)
+t:send_iq(a.iq({to=o.jid,type="get"})
+:tag("query",{xmlns=i}),function(a)
 e=e-1;
 if a.attr.type=="result"then
-local e=a:get_child("query",o)
+local e=a:get_child("query",i)
 :get_child("streamhost").attr;
 t.proxy65.available_streamhosts[e.jid]={
 jid=e.jid;
@@ -4990,98 +6182,93 @@
 end);
 end
 end);
-t:hook("iq/"..o,function(a)
-local e=verse.new(nil,{
-initiator_jid=a.attr.from,
+t:hook("iq/"..i,function(o)
+local e=a.new(nil,{
+initiator_jid=o.attr.from,
 streamhosts={},
 current_host=0;
 });
-for t in a.tags[1]:childtags()do
+for t in o.tags[1]:childtags()do
 if t.name=="streamhost"then
 table.insert(e.streamhosts,t.attr);
 end
 end
-local function o()
+local function i()
 if e.current_host<#e.streamhosts then
 e.current_host=e.current_host+1;
 e:connect(
 e.streamhosts[e.current_host].host,
 e.streamhosts[e.current_host].port
 );
-n(t,e,a.tags[1].attr.sid,a.attr.from,t.jid);
+s(t,e,o.tags[1].attr.sid,o.attr.from,t.jid);
 return true;
 end
-e:unhook("disconnected",o);
-t:send(verse.error_reply(a,"cancel","item-not-found"));
+e:unhook("disconnected",i);
+t:send(a.error_reply(o,"cancel","item-not-found"));
 end
 function e:accept()
-e:hook("disconnected",o,100);
+e:hook("disconnected",i,100);
 e:hook("connected",function()
-e:unhook("disconnected",o);
-local e=verse.reply(a)
-:tag("query",a.tags[1].attr)
+e:unhook("disconnected",i);
+local e=a.reply(o)
+:tag("query",o.tags[1].attr)
 :tag("streamhost-used",{jid=e.streamhosts[e.current_host].jid});
 t:send(e);
 end,100);
-o();
+i();
 end
 function e:refuse()
 end
 t:event("proxy65/request",e);
 end);
 end
-function i:new(t,s)
-local e=verse.new(nil,{
+function n:new(t,n)
+local e=a.new(nil,{
 target_jid=t;
-bytestream_sid=r.generate();
+bytestream_sid=h.generate();
 });
-local a=verse.iq{type="set",to=t}
-:tag("query",{xmlns=o,mode="tcp",sid=e.bytestream_sid});
-for t,e in ipairs(s or self.proxies)do
-a:tag("streamhost",e):up();
-end
-self.stream:send_iq(a,function(a)
-if a.attr.type=="error"then
-local t,a,o=a:get_error();
+local o=a.iq{type="set",to=t}
+:tag("query",{xmlns=i,mode="tcp",sid=e.bytestream_sid});
+for t,e in ipairs(n or self.proxies)do
+o:tag("streamhost",e):up();
+end
+self.stream:send_iq(o,function(o)
+if o.attr.type=="error"then
+local t,a,o=o:get_error();
 e:event("connection-failed",{conn=e,type=t,condition=a,text=o});
 else
-local a=a.tags[1]:get_child("streamhost-used");
-if not a then
-end
-e.streamhost_jid=a.attr.jid;
-local i,a;
-for o,t in ipairs(s or self.proxies)do
+local o=o.tags[1]:get_child("streamhost-used");
+e.streamhost_jid=o.attr.jid;
+local o,h;
+for a,t in ipairs(n or self.proxies)do
 if t.jid==e.streamhost_jid then
-i,a=t.host,t.port;
+o,h=t.host,t.port;
 break;
 end
 end
-if not(i and a)then
-end
-e:connect(i,a);
-local function a()
-e:unhook("connected",a);
-local t=verse.iq{to=e.streamhost_jid,type="set"}
-:tag("query",{xmlns=o,sid=e.bytestream_sid})
+e:connect(o,h);
+local function o()
+e:unhook("connected",o);
+local t=a.iq{to=e.streamhost_jid,type="set"}
+:tag("query",{xmlns=i,sid=e.bytestream_sid})
 :tag("activate"):text(t);
 self.stream:send_iq(t,function(t)
 if t.attr.type=="result"then
 e:event("connected",e);
-else
 end
 end);
 return true;
 end
-e:hook("connected",a,100);
-n(self.stream,e,e.bytestream_sid,self.stream.jid,t);
+e:hook("connected",o,100);
+s(self.stream,e,e.bytestream_sid,self.stream.jid,t);
 end
 end);
 return e;
 end
-function n(i,e,t,o,a)
-local n=h.sha1(t..o..a);
-local function s()
-e:unhook("connected",s);
+function s(i,e,t,o,a)
+local t=r(t..o..a);
+local function i()
+e:unhook("connected",i);
 return true;
 end
 local function o(t)
@@ -5092,44 +6279,44 @@
 e:event("connected");
 return true;
 end
-local function i(a)
-e:unhook("incoming-raw",i);
-if a~="\005\000"then
+local function a(i)
+e:unhook("incoming-raw",a);
+if i~="\005\000"then
 local t="version-mismatch";
-if a:sub(1,1)=="\005"then
+if i:sub(1,1)=="\005"then
 t="authentication-failure";
 end
 return e:event("error",t);
 end
-e:send(string.char(5,1,0,3,#n)..n.."\0\0");
+e:send(string.char(5,1,0,3,#t)..t.."\0\0");
 e:hook("incoming-raw",o,100);
 return true;
 end
-e:hook("connected",s,200);
-e:hook("incoming-raw",i,100);
+e:hook("connected",i,200);
+e:hook("incoming-raw",a,100);
 e:send("\005\001\000");
 end
 end)
 package.preload['verse.plugins.jingle_ibb']=(function(...)
 local e=require"verse";
 local i=require"util.encodings".base64;
-local h=require"util.uuid".generate;
+local s=require"util.uuid".generate;
 local n="urn:xmpp:jingle:transports:ibb:1";
 local o="http://jabber.org/protocol/ibb";
 assert(i.encode("This is a test.")=="VGhpcyBpcyBhIHRlc3Qu","Base64 encoding failed");
 assert(i.decode("VGhpcyBpcyBhIHRlc3Qu")=="This is a test.","Base64 decoding failed");
 local t=table.concat
 local a={};
-local s={__index=a};
-local function r(t)
-local t=setmetatable({stream=t},s)
+local t={__index=a};
+local function h(a)
+local t=setmetatable({stream=a},t)
 t=e.eventable(t);
 return t;
 end
-function a:initiate(t,e,a)
+function a:initiate(a,e,t)
 self.block=2048;
-self.stanza=a or'iq';
-self.peer=t;
+self.stanza=t or'iq';
+self.peer=a;
 self.sid=e or tostring(self):match("%x+$");
 self.iseq=0;
 self.oseq=0;
@@ -5138,10 +6325,10 @@
 end
 self.feeder=e;
 print("Hooking incomming IQs");
-local t=self.stream;
-t:hook("iq/"..o,e)
-if a=="message"then
-t:hook("message",e)
+local a=self.stream;
+a:hook("iq/"..o,e)
+if t=="message"then
+a:hook("message",e)
 end
 end
 function a:open(t)
@@ -5227,7 +6414,7 @@
 end,10);
 local t={};
 function t:_setup()
-local e=r(self.stream);
+local e=h(self.stream);
 e.sid=self.sid or e.sid;
 e.stanza=self.stanza or e.stanza;
 e.block=self.block or e.block;
@@ -5236,7 +6423,7 @@
 end
 function t:generate_initiate()
 print("ibb:generate_initiate() as "..self.role);
-local t=h();
+local t=s();
 self.sid=t;
 self.stanza='iq';
 self.block=2048;
@@ -5289,27 +6476,25 @@
 end
 end)
 package.preload['verse.plugins.pubsub']=(function(...)
-local h=require"verse";
-local e=require"util.jid".bare;
-local n=table.insert;
+local i=require"verse";
+local h=table.insert;
 local o="http://jabber.org/protocol/pubsub";
-local i="http://jabber.org/protocol/pubsub#owner";
-local r="http://jabber.org/protocol/pubsub#event";
-local e="http://jabber.org/protocol/pubsub#errors";
+local s="http://jabber.org/protocol/pubsub#owner";
+local a="http://jabber.org/protocol/pubsub#event";
 local e={};
-local s={__index=e};
-function h.plugins.pubsub(e)
-e.pubsub=setmetatable({stream=e},s);
+local n={__index=e};
+function i.plugins.pubsub(e)
+e.pubsub=setmetatable({stream=e},n);
 e:hook("message",function(t)
-local a=t.attr.from;
-for t in t:childtags("event",r)do
+local o=t.attr.from;
+for t in t:childtags("event",a)do
 local t=t:get_child("items");
 if t then
-local o=t.attr.node;
+local a=t.attr.node;
 for t in t:childtags("item")do
 e:event("pubsub/event",{
-from=a;
-node=o;
+from=o;
+node=a;
 item=t;
 });
 end
@@ -5321,29 +6506,29 @@
 function e:create(e,t,a)
 return self:service(e):node(t):create(nil,a);
 end
-function e:subscribe(o,a,t,e)
-return self:service(o):node(a):subscribe(t,nil,e);
-end
-function e:publish(a,i,o,e,t)
-return self:service(a):node(i):publish(o,nil,e,t);
+function e:subscribe(e,t,a,o)
+return self:service(e):node(t):subscribe(a,nil,o);
+end
+function e:publish(i,o,t,e,a)
+return self:service(i):node(o):publish(t,nil,e,a);
 end
 local a={};
 local t={__index=a};
 function e:service(e)
 return setmetatable({stream=self.stream,service=e},t)
 end
-local function t(r,i,t,a,n,s,e)
-local t=h.iq{type=r or"get",to=i}
-:tag("pubsub",{xmlns=t or o})
-if a then t:tag(a,{node=n,jid=s});end
-if e then t:tag("item",{id=e~=true and e or nil});end
-return t;
+local function t(r,h,e,a,n,s,t)
+local e=i.iq{type=r or"get",to=h}
+:tag("pubsub",{xmlns=e or o})
+if a then e:tag(a,{node=n,jid=s});end
+if t then e:tag("item",{id=t~=true and t or nil});end
+return e;
 end
 function a:subscriptions(a)
 self.stream:send_iq(t(nil,self.service,nil,"subscriptions")
-,a and function(t)
-if t.attr.type=="result"then
-local e=t:get_child("pubsub",o);
+,a and function(i)
+if i.attr.type=="result"then
+local e=i:get_child("pubsub",o);
 local e=e and e:get_child("subscriptions");
 local o={};
 if e then
@@ -5351,32 +6536,32 @@
 local e=self:node(t.attr.node)
 e.subscription=t;
 e.subscribed_jid=t.attr.jid;
-n(o,e);
+h(o,e);
 end
 end
 a(o);
 else
-a(false,t:get_error());
+a(false,i:get_error());
 end
 end or nil);
 end
-function a:affiliations(a)
+function a:affiliations(e)
 self.stream:send_iq(t(nil,self.service,nil,"affiliations")
-,a and function(e)
-if e.attr.type=="result"then
-local e=e:get_child("pubsub",o);
-local e=e and e:get_child("affiliations")or{};
-local t={};
-if e then
-for a in e:childtags("affiliation")do
-local e=self:node(a.attr.node)
-e.affiliation=a;
-n(t,e);
-end
-end
-a(t);
-else
-a(false,e:get_error());
+,e and function(t)
+if t.attr.type=="result"then
+local t=t:get_child("pubsub",o);
+local t=t and t:get_child("affiliations")or{};
+local a={};
+if t then
+for t in t:childtags("affiliation")do
+local e=self:node(t.attr.node)
+e.affiliation=t;
+h(a,e);
+end
+end
+e(a);
+else
+e(false,t:get_error());
 end
 end or nil);
 end
@@ -5395,7 +6580,7 @@
 function a:node(e)
 return setmetatable({stream=self.stream,service=self.service,node=e},o)
 end
-function s:__call(t,e)
+function n:__call(t,e)
 local t=self:service(t);
 return e and t:node(e)or t;
 end
@@ -5433,21 +6618,21 @@
 end
 self.stream:send_iq(t("set",self.service,nil,e==nil and"default"or"configure",self.node),a);
 end
-function e:publish(o,a,e,i)
+function e:publish(i,a,e,o)
 if a~=nil then
 error("Node configuration is not implemented yet.");
 end
-self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,o or true)
+self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,i or true)
 :add_child(e)
-,i);
-end
-function e:subscribe(e,a,o)
+,o);
+end
+function e:subscribe(e,o,a)
 e=e or self.stream.jid;
-if a~=nil then
+if o~=nil then
 error("Subscription configuration is not implemented yet.");
 end
-self.stream:send_iq(t("set",self.service,nil,"subscribe",self.node,e,id)
-,o);
+self.stream:send_iq(t("set",self.service,nil,"subscribe",self.node,e)
+,a);
 end
 function e:subscription(e)
 error("Not implemented yet.");
@@ -5471,22 +6656,22 @@
 self.stream:disco_items(self.service,self.node,e);
 end
 end
-function e:item(a,e)
-self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,a)
-,e);
+function e:item(e,a)
+self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,e)
+,a);
 end
 function e:retract(e,a)
 self.stream:send_iq(t("set",self.service,nil,"retract",self.node,nil,e)
 ,a);
 end
-function e:purge(e,a)
-assert(not e,"Not implemented yet.");
-self.stream:send_iq(t("set",self.service,i,"purge",self.node)
-,a);
+function e:purge(a,e)
+assert(not a,"Not implemented yet.");
+self.stream:send_iq(t("set",self.service,s,"purge",self.node)
+,e);
 end
 function e:delete(a,e)
 assert(not a,"Not implemented yet.");
-self.stream:send_iq(t("set",self.service,i,"delete",self.node)
+self.stream:send_iq(t("set",self.service,s,"delete",self.node)
 ,e);
 end
 end)
@@ -5532,24 +6717,24 @@
 e:add_plugin("disco");
 e:add_disco_feature(t);
 function e:query_commands(a,o)
-e:disco_items(a,t,function(a)
+e:disco_items(a,t,function(t)
 e:debug("adhoc list returned")
-local t={};
-for o,a in ipairs(a)do
-t[a.node]=a.name;
+local a={};
+for o,t in ipairs(t)do
+a[t.node]=t.name;
 end
 e:debug("adhoc calling callback")
-return o(t);
+return o(a);
 end);
 end
-function e:execute_command(t,o,i)
+function e:execute_command(i,o,t)
 local e=setmetatable({
-stream=e,jid=t,
-command=o,callback=i
+stream=e,jid=i,
+command=o,callback=t
 },a);
 return e:execute();
 end
-local function r(t,e)
+local function s(t,e)
 if not(e)or e=="user"then return true;end
 if type(e)=="function"then
 return e(t);
@@ -5560,24 +6745,24 @@
 e:add_disco_item({jid=e.jid,node=a,name=o},t);
 return i[a];
 end
-local function s(a)
-local t=a.tags[1];
-local t=t.attr.node;
-local t=i[t];
-if not t then return;end
-if not r(a.attr.from,t.permission)then
-e:send(o.error_reply(a,"auth","forbidden","You don't have permission to execute this command"):up()
-:add_child(t:cmdtag("canceled")
+local function h(t)
+local a=t.tags[1];
+local a=a.attr.node;
+local a=i[a];
+if not a then return;end
+if not s(t.attr.from,a.permission)then
+e:send(o.error_reply(t,"auth","forbidden","You don't have permission to execute this command"):up()
+:add_child(a:cmdtag("canceled")
 :tag("note",{type="error"}):text("You don't have permission to execute this command")));
 return true
 end
-return n.handle_cmd(t,{send=function(t)return e:send(t)end},a);
+return n.handle_cmd(a,{send=function(t)return e:send(t)end},t);
 end
 e:hook("iq/"..t,function(e)
-local a=e.attr.type;
-local t=e.tags[1].name;
-if a=="set"and t=="command"then
-return s(e);
+local t=e.attr.type;
+local a=e.tags[1].name;
+if t=="set"and a=="command"then
+return h(e);
 end
 end);
 end
@@ -5601,57 +6786,57 @@
 self:_process_response(e);
 end);
 end
-function a:next(e)
-local t=o.iq({to=self.jid,type="set"})
+function a:next(a)
+local e=o.iq({to=self.jid,type="set"})
 :tag("command",{
 xmlns=t,
 node=self.command,
 sessionid=self.sessionid
 });
-if e then t:add_child(e);end
-self.stream:send_iq(t,function(e)
+if a then e:add_child(a);end
+self.stream:send_iq(e,function(e)
 self:_process_response(e);
 end);
 end
 end)
 package.preload['verse.plugins.presence']=(function(...)
 local a=require"verse";
-function a.plugins.presence(e)
-e.last_presence=nil;
-e:hook("presence-out",function(t)
-if not t.attr.to then
-e.last_presence=t;
+function a.plugins.presence(t)
+t.last_presence=nil;
+t:hook("presence-out",function(e)
+if not e.attr.to then
+t.last_presence=e;
 end
 end,1);
-function e:resend_presence()
+function t:resend_presence()
 if last_presence then
-e:send(last_presence);
-end
-end
-function e:set_status(t)
+t:send(last_presence);
+end
+end
+function t:set_status(e)
 local a=a.presence();
-if type(t)=="table"then
-if t.show then
-a:tag("show"):text(t.show):up();
-end
-if t.prio then
-a:tag("priority"):text(tostring(t.prio)):up();
-end
-if t.msg then
-a:tag("status"):text(t.msg):up();
-end
-end
-e:send(a);
+if type(e)=="table"then
+if e.show then
+a:tag("show"):text(e.show):up();
+end
+if e.prio then
+a:tag("priority"):text(tostring(e.prio)):up();
+end
+if e.msg then
+a:tag("status"):text(e.msg):up();
+end
+end
+t:send(a);
 end
 end
 end)
 package.preload['verse.plugins.private']=(function(...)
-local a=require"verse";
-local t="jabber:iq:private";
-function a.plugins.private(i)
-function i:private_set(o,i,e,n)
-local t=a.iq({type="set"})
-:tag("query",{xmlns=t});
+local t=require"verse";
+local a="jabber:iq:private";
+function t.plugins.private(o)
+function o:private_set(o,i,e,n)
+local t=t.iq({type="set"})
+:tag("query",{xmlns=a});
 if e then
 if e.name==o and e.attr and e.attr.xmlns==i then
 t:add_child(e);
@@ -5662,13 +6847,13 @@
 end
 self:send_iq(t,n);
 end
-function i:private_get(e,o,i)
-self:send_iq(a.iq({type="get"})
-:tag("query",{xmlns=t})
+function o:private_get(e,o,i)
+self:send_iq(t.iq({type="get"})
+:tag("query",{xmlns=a})
 :tag(e,{xmlns=o}),
-function(a)
-if a.attr.type=="result"then
-local t=a:get_child("query",t);
+function(t)
+if t.attr.type=="result"then
+local t=t:get_child("query",a);
 local e=t:get_child(e,o);
 i(e);
 end
@@ -5677,25 +6862,25 @@
 end
 end)
 package.preload['verse.plugins.roster']=(function(...)
-local o=require"verse";
+local i=require"verse";
 local d=require"util.jid".bare;
 local a="jabber:iq:roster";
-local n="urn:xmpp:features:rosterver";
-local i=table.insert;
-function o.plugins.roster(t)
-local s=false;
+local o="urn:xmpp:features:rosterver";
+local n=table.insert;
+function i.plugins.roster(t)
+local r=false;
 local e={
 items={};
 ver="";
 };
 t.roster=e;
 t:hook("stream-features",function(e)
-if e:get_child("ver",n)then
-s=true;
+if e:get_child("ver",o)then
+r=true;
 end
 end);
-local function h(t)
-local e=o.stanza("item",{xmlns=a});
+local function s(t)
+local e=i.stanza("item",{xmlns=a});
 for a,t in pairs(t)do
 if a~="groups"then
 e.attr[a]=t;
@@ -5707,18 +6892,17 @@
 end
 return e;
 end
-local function r(t)
+local function l(o)
 local e={};
 local a={};
 e.groups=a;
-local o=t.attr.jid;
-for t,a in pairs(t.attr)do
+for t,a in pairs(o.attr)do
 if t~="xmlns"then
 e[t]=a
 end
 end
-for e in t:childtags("group")do
-i(a,e:get_text())
+for e in o:childtags("group")do
+n(a,e:get_text())
 end
 return e;
 end
@@ -5731,49 +6915,47 @@
 items=e.items,
 };
 end
-function e:add_contact(s,i,n,e)
-local i={jid=s,name=i,groups=n};
-local a=o.iq({type="set"})
+function e:add_contact(o,n,h,e)
+local o={jid=o,name=n,groups=h};
+local a=i.iq({type="set"})
 :tag("query",{xmlns=a})
-:add_child(h(i));
+:add_child(s(o));
 t:send_iq(a,function(t)
 if not e then return end
 if t.attr.type=="result"then
 e(true);
 else
-local a,o,t=t:get_error();
-e(nil,{a,o,t});
+e(nil,t);
 end
 end);
 end
-function e:delete_contact(i,n)
-i=(type(i)=="table"and i.jid)or i;
-local s={jid=i,subscription="remove"}
-if not e.items[i]then return false,"item-not-found";end
-t:send_iq(o.iq({type="set"})
+function e:delete_contact(o,n)
+o=(type(o)=="table"and o.jid)or o;
+local h={jid=o,subscription="remove"}
+if not e.items[o]then return false,"item-not-found";end
+t:send_iq(i.iq({type="set"})
 :tag("query",{xmlns=a})
-:add_child(h(s)),
+:add_child(s(h)),
 function(e)
 if not n then return end
 if e.attr.type=="result"then
 n(true);
 else
-local e,t,a=e:get_error();
-n(nil,{e,t,a});
+n(nil,e);
 end
 end);
 end
 local function h(t)
-local t=r(t);
+local t=l(t);
 e.items[t.jid]=t;
 end
-local function r(t)
+local function l(t)
 local a=e.items[t];
 e.items[t]=nil;
 return a;
 end
-function e:fetch(i)
-t:send_iq(o.iq({type="get"}):tag("query",{xmlns=a,ver=s and e.ver or nil}),
+function e:fetch(o)
+t:send_iq(i.iq({type="get"}):tag("query",{xmlns=a,ver=r and e.ver or nil}),
 function(t)
 if t.attr.type=="result"then
 local t=t:get_child("query",a);
@@ -5784,35 +6966,34 @@
 end
 e.ver=t.attr.ver or"";
 end
-i(e);
-else
-local e,t,a=stanza:get_error();
-i(nil,{e,t,a});
+o(e);
+else
+o(nil,t);
 end
 end);
 end
-t:hook("iq/"..a,function(i)
-local s,n=i.attr.type,i.attr.from;
-if s=="set"and(not n or n==d(t.jid))then
-local s=i:get_child("query",a);
-local a=s and s:get_child("item");
-if a then
-local n,o;
-local i=a.attr.jid;
-if a.attr.subscription=="remove"then
+t:hook("iq/"..a,function(n)
+local s,o=n.attr.type,n.attr.from;
+if s=="set"and(not o or o==d(t.jid))then
+local s=n:get_child("query",a);
+local o=s and s:get_child("item");
+if o then
+local n,a;
+local i=o.attr.jid;
+if o.attr.subscription=="remove"then
 n="removed"
-o=r(i);
+a=l(i);
 else
 n=e.items[i]and"changed"or"added";
-h(a)
-o=e.items[i];
+h(o)
+a=e.items[i];
 end
 e.ver=s.attr.ver;
-if o then
-t:event("roster/item-"..n,o);
-end
-end
-t:send(o.reply(i))
+if a then
+t:event("roster/item-"..n,a);
+end
+end
+t:send(i.reply(n))
 return true;
 end
 end);
@@ -5835,9 +7016,9 @@
 if t.attr.type=="result"then
 e:event("registration-success");
 else
-local o,t,a=t:get_error();
+local a,t,o=t:get_error();
 e:debug("Registration failed: %s",t);
-e:event("registration-failure",{type=o,condition=t,text=a});
+e:event("registration-failure",{type=a,condition=t,text=o});
 end
 end);
 else
@@ -5870,17 +7051,17 @@
 end
 if t and t.opts.source and e.attr.to~=t.opts.source then return end
 if t then
-local o=select(3,n.split(e.attr.from));
+local i=select(3,n.split(e.attr.from));
 local n=e:get_child_text("body");
-local i=e:get_child("delay",h);
+local o=e:get_child("delay",h);
 local a={
 room_jid=a;
 room=t;
-sender=t.occupants[o];
-nick=o;
+sender=t.occupants[i];
+nick=i;
 body=n;
 stanza=e;
-delay=(i and i.attr.stamp);
+delay=(o and o.attr.stamp);
 };
 local t=t:event(e.name,a);
 return t or(e.name=="message")or nil;
@@ -5986,48 +7167,47 @@
 function a:set_subject(e)
 self:send(i.message():tag("subject"):text(e));
 end
-function a:leave(e)
+function a:leave(t)
 self.stream:event("groupchat/leaving",self);
-local t=i.presence({type="unavailable"});
-if e then
-t:tag("status"):text(e);
-end
-self:send(t);
-end
-function a:admin_set(t,e,a,o)
+local e=i.presence({type="unavailable"});
+if t then
+e:tag("status"):text(t);
+end
+self:send(e);
+end
+function a:admin_set(e,a,t,o)
 self:send(i.iq({type="set"})
 :query(s.."#admin")
-:tag("item",{nick=t,[e]=a})
+:tag("item",{nick=e,[a]=t})
 :tag("reason"):text(o or""));
 end
-function a:set_role(a,t,e)
-self:admin_set(a,"role",t,e);
-end
-function a:set_affiliation(a,e,t)
-self:admin_set(a,"affiliation",e,t);
+function a:set_role(e,a,t)
+self:admin_set(e,"role",a,t);
+end
+function a:set_affiliation(e,t,a)
+self:admin_set(e,"affiliation",t,a);
 end
 function a:kick(e,t)
 self:set_role(e,"none",t);
 end
-function a:ban(e,t)
-self:set_affiliation(e,"outcast",t);
+function a:ban(t,e)
+self:set_affiliation(t,"outcast",e);
 end
 end)
 package.preload['verse.plugins.vcard']=(function(...)
 local i=require"verse";
 local o=require"util.vcard";
-local n="vcard-temp";
+local e="vcard-temp";
 function i.plugins.vcard(a)
-function a:get_vcard(t,e)
-a:send_iq(i.iq({to=t,type="get"})
-:tag("vCard",{xmlns=n}),e and function(t)
-local a,a;
-vCard=t:get_child("vCard",n);
-if t.attr.type=="result"and vCard then
-vCard=o.from_xep54(vCard)
-e(vCard)
-else
-e(false)
+function a:get_vcard(n,t)
+a:send_iq(i.iq({to=n,type="get"})
+:tag("vCard",{xmlns=e}),t and function(a)
+local e=a:get_child("vCard",e);
+if a.attr.type=="result"and e then
+e=o.from_xep54(e)
+t(e)
+else
+t(false)
 end
 end or nil);
 end
@@ -6050,15 +7230,8 @@
 end)
 package.preload['verse.plugins.vcard_update']=(function(...)
 local n=require"verse";
-local e,i="vcard-temp","vcard-temp:x:update";
-local e,t=pcall(function()return require("util.hashes").sha1;end);
-if not e then
-e,t=pcall(function()return require("util.sha1").sha1;end);
-if not e then
-error("Could not find a sha1()")
-end
-end
-local s=t;
+local i="vcard-temp:x:update";
+local s=require("util.hashes").sha1;
 local e,t=pcall(function()
 local e=require("util.encodings").base64.decode;
 assert(e("SGVsbG8=")=="Hello")
@@ -6075,7 +7248,7 @@
 e:add_plugin("vcard");
 e:add_plugin("presence");
 local t;
-function update_vcard_photo(o)
+local function r(o)
 local a;
 for e=1,#o do
 if o[e].name=="PHOTO"then
@@ -6092,14 +7265,13 @@
 t=nil;
 end
 end
-local a=e.set_vcard;
 local a;
-e:hook("ready",function(t)
+e:hook("ready",function()
 if a then return;end
 a=true;
 e:get_vcard(nil,function(t)
 if t then
-update_vcard_photo(t)
+r(t)
 end
 e:event("ready");
 end);
@@ -6115,10 +7287,10 @@
 package.preload['verse.plugins.carbons']=(function(...)
 local o=require"verse";
 local a="urn:xmpp:carbons:2";
-local h="urn:xmpp:forward:0";
-local n=os.time;
+local r="urn:xmpp:forward:0";
+local h=os.time;
 local s=require"util.datetime".parse;
-local r=require"util.jid".bare;
+local n=require"util.jid".bare;
 function o.plugins.carbons(e)
 local t={};
 t.enabled=false;
@@ -6149,15 +7321,15 @@
 end
 end or nil);
 end
-local i;
+local o;
 e:hook("bind-success",function()
-i=r(e.jid);
+o=n(e.jid);
 end);
-e:hook("message",function(o)
-local t=o:get_child(nil,a);
-if o.attr.from==i and t then
+e:hook("message",function(i)
+local t=i:get_child(nil,a);
+if i.attr.from==o and t then
 local o=t.name;
-local t=t:get_child("forwarded",h);
+local t=t:get_child("forwarded",r);
 local a=t and t:get_child("message","jabber:client");
 local t=t:get_child("delay","urn:xmpp:delay");
 local t=t and t.attr.stamp;
@@ -6166,7 +7338,7 @@
 return e:event("carbon",{
 dir=o,
 stanza=a,
-timestamp=t or n(),
+timestamp=t or h(),
 });
 end
 end
@@ -6174,52 +7346,52 @@
 end
 end)
 package.preload['verse.plugins.archive']=(function(...)
-local t=require"verse";
-local e=require"util.stanza";
-local a="urn:xmpp:mam:0"
-local s="urn:xmpp:forward:0";
+local a=require"verse";
+local t=require"util.stanza";
+local e="urn:xmpp:mam:0"
+local h="urn:xmpp:forward:0";
 local l="urn:xmpp:delay";
-local i=require"util.uuid".generate;
-local m=require"util.datetime".parse;
-local h=require"util.datetime".datetime;
-local o=require"util.dataforms".new;
+local o=require"util.uuid".generate;
+local u=require"util.datetime".parse;
+local n=require"util.datetime".datetime;
+local i=require"util.dataforms".new;
 local r=require"util.rsm";
 local c={};
-local u=o{
-{name="FORM_TYPE";type="hidden";value=a;};
+local m=i{
+{name="FORM_TYPE";type="hidden";value=e;};
 {name="with";type="jid-single";};
 {name="start";type="text-single"};
 {name="end";type="text-single";};
 };
-function t.plugins.archive(n)
-function n:query_archive(o,t,d)
-local i=i();
-local o=e.iq{type="set",to=o}
-:tag("query",{xmlns=a,queryid=i});
-local e,n=tonumber(t["start"]),tonumber(t["end"]);
-t["start"]=e and h(e);
-t["end"]=n and h(n);
-o:add_child(u:form(t,"submit"));
-o:add_child(r.generate(t));
-local t={};
+function a.plugins.archive(s)
+function s:query_archive(d,a,s)
+local i=o();
+local o=t.iq{type="set",to=d}
+:tag("query",{xmlns=e,queryid=i});
+local t,d=tonumber(a["start"]),tonumber(a["end"]);
+a["start"]=t and n(t);
+a["end"]=d and n(d);
+o:add_child(m:form(a,"submit"));
+o:add_child(r.generate(a));
+local a={};
 local function n(o)
-local e=o:get_child("fin",a)
+local t=o:get_child("fin",e)
+if t and t.attr.queryid==i then
+local e=r.get(t);
+for t,e in pairs(e or c)do a[t]=e;end
+self:unhook("message",n);
+s(a);
+return true
+end
+local e=o:get_child("result",e);
 if e and e.attr.queryid==i then
-local e=r.get(e);
-for a,e in pairs(e or c)do t[a]=e;end
-self:unhook("message",n);
-d(t);
-return true
-end
-local e=o:get_child("result",a);
-if e and e.attr.queryid==i then
-local a=e:get_child("forwarded",s);
-a=a or o:get_child("forwarded",s);
+local t=e:get_child("forwarded",h);
+t=t or o:get_child("forwarded",h);
 local o=e.attr.id;
-local e=a:get_child("delay",l);
-local e=e and m(e.attr.stamp)or nil;
-local a=a:get_child("message","jabber:client")
-t[#t+1]={id=o,stamp=e,message=a};
+local e=t:get_child("delay",l);
+local e=e and u(e.attr.stamp)or nil;
+local t=t:get_child("message","jabber:client")
+a[#a+1]={id=o,stamp=e,message=t};
 return true
 end
 end
@@ -6228,7 +7400,7 @@
 if e.attr.type=="error"then
 self:warn(table.concat({e:get_error()}," "))
 self:unhook("message",n);
-d(false,e:get_error())
+s(false,e:get_error())
 end
 return true
 end);
@@ -6260,281 +7432,46 @@
 end
 return e;
 end
-local function s(o)
-local t
-t,o[false]=o[false],nil;
-if t~=nil then
-t=i[t];
-end
-local i=e.stanza("prefs",{xmlns=a,default=t})
-local t=e.stanza("always");
-local e=e.stanza("never");
-for a,o in pairs(o)do
-(o and t or e):tag("jid"):text(a):up();
-end
-return i:add_child(t):add_child(e);
-end
-function n:archive_prefs_get(t)
-self:send_iq(e.iq{type="get"}:tag("prefs",{xmlns=a}),
+local function n(o)
+local a
+a,o[false]=o[false],nil;
+if a~=nil then
+a=i[a];
+end
+local i=t.stanza("prefs",{xmlns=e,default=a})
+local a=t.stanza("always");
+local e=t.stanza("never");
+for o,t in pairs(o)do
+(t and a or e):tag("jid"):text(o):up();
+end
+return i:add_child(a):add_child(e);
+end
+function s:archive_prefs_get(a)
+self:send_iq(t.iq{type="get"}:tag("prefs",{xmlns=e}),
 function(e)
 if e and e.attr.type=="result"and e.tags[1]then
-local a=h(e.tags[1]);
-t(a,e);
-else
-t(nil,e);
+local t=h(e.tags[1]);
+a(t,e);
+else
+a(nil,e);
 end
 end);
 end
-function n:archive_prefs_set(t,a)
-self:send_iq(e.iq{type="set"}:add_child(s(t)),a);
+function s:archive_prefs_set(a,e)
+self:send_iq(t.iq{type="set"}:add_child(n(a)),e);
 end
 end
 end)
-package.preload['net.httpclient_listener']=(function(...)
-local n=require"util.logger".init("httpclient_listener");
-local i,h=table.concat,table.insert;
-local s=require"net.connlisteners".register;
-local t={};
-local e={};
-local o={default_port=80,default_mode="*a"};
-function o.onconnect(a)
-local e=t[a];
-local t={e.method or"GET"," ",e.path," HTTP/1.1\r\n"};
-if e.query then
-h(t,4,"?"..e.query);
-end
-a:write(i(t));
-local t={[2]=": ",[4]="\r\n"};
-for o,e in pairs(e.headers)do
-t[1],t[3]=o,e;
-a:write(i(t));
-end
-a:write("\r\n");
-if e.body then
-a:write(e.body);
-end
-end
-function o.onincoming(o,a)
-local e=t[o];
-if not e then
-n("warn","Received response from connection %s with no request attached!",tostring(o));
-return;
-end
-if a and e.reader then
-e:reader(a);
-end
-end
-function o.ondisconnect(a,e)
-local e=t[a];
-if e and e.conn then
-e:reader(nil);
-end
-t[a]=nil;
-end
-function o.register_request(a,e)
-n("debug","Attaching request %s to connection %s",tostring(e.id or e),tostring(a));
-t[a]=e;
-end
-s("httpclient",o);
-end)
-package.preload['net.connlisteners']=(function(...)
-local h=(CFG_SOURCEDIR or".").."/net/";
-local u=require"net.server";
-local o=require"util.logger".init("connlisteners");
-local i=tostring;
-local d=type
-local r=ipairs
-local n,c,s=
-dofile,xpcall,error
-local l=debug.traceback;
-module"connlisteners"
-local e={};
-function register(t,a)
-if e[t]and e[t]~=a then
-o("debug","Listener %s is already registered, not registering any more",t);
-return false;
-end
-e[t]=a;
-o("debug","Registered connection listener %s",t);
-return true;
-end
-function deregister(t)
-e[t]=nil;
-end
-function get(t)
-local a=e[t];
-if not a then
-local s,n=c(function()n(h..t:gsub("[^%w%-]","_").."_listener.lua")end,l);
-if not s then
-o("error","Error while loading listener '%s': %s",i(t),i(n));
-return nil,n;
-end
-a=e[t];
-end
-return a;
-end
-function start(i,e)
-local a,t=get(i);
-if not a then
-s("No such connection module: "..i..(t and(" ("..t..")")or""),0);
-end
-local o=(e and e.interface)or a.default_interface or"*";
-if d(o)=="string"then o={o};end
-local h=(e and e.port)or a.default_port or s("Can't start listener "..i.." because no port was specified, and it has no default port",0);
-local s=(e and e.mode)or a.default_mode or 1;
-local n=(e and e.ssl)or nil;
-local i=e and e.type=="ssl";
-if i and not n then
-return nil,"no ssl context";
-end
-ok,t=true,{};
-for e,o in r(o)do
-local e
-e,t[o]=u.addserver(o,h,a,s,i and n or nil);
-ok=ok and e;
-end
-return ok,t;
-end
-return _M;
-end)
-package.preload['util.httpstream']=(function(...)
-local t=coroutine;
-local n=tonumber;
-local h=t.create(function()end);
-t.resume(h);
-module("httpstream")
-local function c(l,o,d)
-local e=t.yield();
-local function i()
-local a=e:find("\r\n",nil,true);
-while not a do
-e=e..t.yield();
-a=e:find("\r\n",nil,true);
-end
-local t=e:sub(1,a-1);
-e=e:sub(a+2);
-return t;
-end
-local function h(a)
-while#e<a do
-e=e..t.yield();
-end
-local t=e:sub(1,a);
-e=e:sub(a+1);
-return t;
-end
-local function r()
-local a={};
-while true do
-local e=i();
-if e==""then break;end
-local e,o=e:match("^([^%s:]+): *(.*)$");
-if not e then t.yield("invalid-header-line");end
-e=e:lower();
-a[e]=a[e]and a[e]..","..o or o;
-end
-return a;
-end
-if not o or o=="server"then
-while true do
-local e=i();
-local a,e,i=e:match("^(%S+)%s+(%S+)%s+HTTP/(%S+)$");
-if not a then t.yield("invalid-status-line");end
-e=e:gsub("^//+","/");
-local o=r();
-local t=n(o["content-length"]);
-t=t or 0;
-local t=h(t);
-l({
-method=a;
-path=e;
-httpversion=i;
-headers=o;
-body=t;
-});
-end
-elseif o=="client"then
-while true do
-local a=i();
-local u,a,o=a:match("^HTTP/(%S+)%s+(%d%d%d)%s+(.*)$");
-a=n(a);
-if not a then t.yield("invalid-status-line");end
-local s=r();
-local d=not
-((d and d().method=="HEAD")
-or(a==204 or a==304 or a==301)
-or(a>=100 and a<200));
-local o;
-if d then
-local a=n(s["content-length"]);
-if s["transfer-encoding"]=="chunked"then
-o="";
-while true do
-local e=i():match("^%x+");
-if not e then t.yield("invalid-chunk-size");end
-e=n(e,16)
-if e==0 then break;end
-o=o..h(e);
-if i()~=""then t.yield("invalid-chunk-ending");end
-end
-local e=r();
-elseif a then
-o=h(a);
-else
-repeat
-local t=t.yield();
-e=e..t;
-until t=="";
-o,e=e,"";
-end
-end
-l({
-code=a;
-httpversion=u;
-headers=s;
-body=o;
-responseversion=u;
-responseheaders=s;
-});
-end
-else t.yield("unknown-parser-type");end
-end
-function new(n,i,o,a)
-local e=t.create(c);
-t.resume(e,n,o,a)
-return{
-feed=function(n,a)
-if not a then
-if o=="client"then t.resume(e,"");end
-e=h;
-return i();
-end
-local a,t=t.resume(e,a);
-if t then
-e=h;
-return i(t);
-end
-end;
-};
-end
-return _M;
-end)
-package.preload['net.http']=(function(...)
-local c=require"socket"
-local u=require"mime"
-local h=require"socket.url"
-local f=require"util.httpstream".new;
-local m=require"net.server"
-local e=require"net.connlisteners".get;
-local n=e("httpclient")or error("No httpclient listener!");
-local o,s=table.insert,table.concat;
-local i,p=pairs,ipairs;
-local d,l,w,y,v,a,t=
-tonumber,tostring,xpcall,select,debug.traceback,string.char,string.format;
-local r=require"util.logger".init("http");
-module"http"
-function urlencode(e)return e and(e:gsub("%W",function(e)return t("%%%02x",e:byte());end));end
-function urldecode(e)return e and(e:gsub("%%(%x%x)",function(e)return a(d(e,16));end));end
+package.preload['util.http']=(function(...)
+local t,h=string.format,string.char;
+local r,s,i=pairs,ipairs,tonumber;
+local o,d=table.insert,table.concat;
+local function n(e)
+return e and(e:gsub("[^a-zA-Z0-9.~_-]",function(e)return t("%%%02x",e:byte());end));
+end
+local function a(e)
+return e and(e:gsub("%%(%x%x)",function(e)return h(i(e,16));end));
+end
 local function e(e)
 return e and(e:gsub("%W",function(e)
 if e~=" "then
@@ -6544,126 +7481,436 @@
 end
 end));
 end
-function formencode(t)
+local function h(t)
 local a={};
 if t[1]then
-for i,t in p(t)do
+for i,t in s(t)do
 o(a,e(t.name).."="..e(t.value));
 end
 else
-for t,i in i(t)do
+for t,i in r(t)do
 o(a,e(t).."="..e(i));
 end
 end
-return s(a,"&");
-end
-function formdecode(e)
-if not e:match("=")then return urldecode(e);end
-local a={};
+return d(a,"&");
+end
+local function s(e)
+if not e:match("=")then return a(e);end
+local i={};
 for t,e in e:gmatch("([^=&]*)=([^&]*)")do
 t,e=t:gsub("%+","%%20"),e:gsub("%+","%%20");
-t,e=urldecode(t),urldecode(e);
-o(a,{name=t,value=e});
-a[t]=e;
-end
-return a;
-end
-local function p(e,a,t)
+t,e=a(t),a(e);
+o(i,{name=t,value=e});
+i[t]=e;
+end
+return i;
+end
+local function t(e,t)
+e=","..e:gsub("[ \t]",""):lower()..",";
+return e:find(","..t:lower()..",",1,true)~=nil;
+end
+return{
+urlencode=n,urldecode=a;
+formencode=h,formdecode=s;
+contains_token=t;
+};
+end)
+package.preload['net.http.parser']=(function(...)
+local c=tonumber;
+local a=assert;
+local g,u=table.insert,table.concat;
+local q=require"socket.url".parse;
+local t=require"util.http".urldecode;
+local function k(e)
+e=t((e:gsub("//+","/")));
+if e:sub(1,1)~="/"then
+e="/"..e;
+end
+local t=0;
+for e in e:gmatch("([^/]+)/")do
+if e==".."then
+t=t-1;
+elseif e~="."then
+t=t+1;
+end
+if t<0 then
+return nil;
+end
+end
+return e;
+end
+local v={};
+function v.new(f,r,e,l)
+local m=true;
+if not e or e=="server"then m=false;else a(e=="client","Invalid parser type");end
+local e,o,i={},0,true;
+local v=c(l and l().body_size_limit)or 10*1024*1024;
+local p=c(l and l().buffer_size_limit)or v*2;
+local b,a,h;
+local d=nil;
+local n;
+local t;
+local y;
+local s;
+return{
+feed=function(j,w)
+if s then return nil,"parse has failed";end
+if not w then
+if i then e,i=u(e),false;end
+if d and m and not t then
+n.body=e;
+f(n);
+elseif e~=""then
+s=true;return r("unexpected-eof");
+end
+return;
+end
+if i then
+g(e,w);
+else
+e={e,w};
+i=true;
+end
+o=o+#w;
+if o>p then s=true;return r("max-buffer-size-exceeded");end
+while o>0 do
+if d==nil then
+if i then e,i=u(e),false;end
+local f=e:find("\r\n\r\n",nil,true);
+if not f then return;end
+local w,h,u,i,g;
+local p;
+local a={};
+for t in e:sub(1,f+1):gmatch("([^\r\n]+)\r\n")do
+if p then
+local e,t=t:match("^([^%s:]+): *(.*)$");
+if not e then s=true;return r("invalid-header-line");end
+e=e:lower();
+a[e]=a[e]and a[e]..","..t or t;
+else
+p=t;
+if m then
+u,i,g=t:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$");
+i=c(i);
+if not i then s=true;return r("invalid-status-line");end
+y=not
+((l and l().method=="HEAD")
+or(i==204 or i==304 or i==301)
+or(i>=100 and i<200));
+else
+w,h,u=t:match("^(%w+) (%S+) HTTP/(1%.[01])$");
+if not w then s=true;return r("invalid-status-line");end
+end
+end
+end
+if not p then s=true;return r("invalid-status-line");end
+b=y and a["transfer-encoding"]=="chunked";
+t=c(a["content-length"]);
+if t and t>v then s=true;return r("content-length-limit-exceeded");end
+if m then
+if not y then t=0;end
+n={
+code=i;
+httpversion=u;
+headers=a;
+body=y and""or nil;
+responseversion=u;
+responseheaders=a;
+};
+else
+local e;
+if h:byte()==47 then
+local a,t=h:match("([^?]*).?(.*)");
+if t==""then t=nil;end
+e={path=a,query=t};
+else
+e=q(h);
+if not(e and e.path)then s=true;return r("invalid-url");end
+end
+h=k(e.path);
+a.host=e.host or a.host;
+t=t or 0;
+n={
+method=w;
+url=e;
+path=h;
+httpversion=u;
+headers=a;
+body=nil;
+};
+end
+e=e:sub(f+4);
+o=#e;
+d=true;
+end
+if d then
+if m then
+if b then
+if h and o-h-2<a then
+return;
+end
+if i then e,i=u(e),false;end
+if not e:find("\r\n",nil,true)then
+return;
+end
+if not a then
+a,h=e:match("^(%x+)[^\r\n]*\r\n()");
+a=a and c(a,16);
+if not a then s=true;return r("invalid-chunk-size");end
+end
+if a==0 and e:find("\r\n\r\n",h-2,true)then
+d,a=nil,nil;
+e=e:gsub("^.-\r\n\r\n","");
+f(n);
+elseif o-h-2>=a then
+n.body=n.body..e:sub(h,h+(a-1));
+e=e:sub(h+a+2);
+o=o-(h+a+2-1);
+a,h=nil,nil;
+else
+break;
+end
+elseif t and o>=t then
+if i then e,i=u(e),false;end
+if n.code==101 then
+n.body,e,o,i=e,{},0,true;
+else
+n.body,e=e:sub(1,t),e:sub(t+1);
+o=#e;
+end
+d=nil;f(n);
+else
+break;
+end
+elseif o>=t then
+if i then e,i=u(e),false;end
+n.body,e=e:sub(1,t),e:sub(t+1);
+o=#e;
+d=nil;f(n);
+else
+break;
+end
+end
+end
+end;
+};
+end
+return v;
+end)
+package.preload['net.http']=(function(...)
+local z=require"util.encodings".base64.encode;
+local m=require"socket.url"
+local h=require"net.http.parser".new;
+local u=require"util.http";
+local g=require"util.events";
+local b=pcall(require,"ssl");
+local k=require"net.server"
+local i,o=table.insert,table.concat;
+local w=pairs;
+local v,l,p,s=
+tonumber,tostring,xpcall,debug.traceback;
+local y=error
+local d=require"util.logger".init("http");
+local e=nil;
+local n={};
+local function c(e)return(l(e):match("%x+$"));end
+local r={default_port=80,default_mode="*a"};
+function r.onconnect(t)
+local e=n[t];
+local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"};
+if e.query then
+i(a,4,"?"..e.query);
+end
+t:write(o(a));
+local a={[2]=": ",[4]="\r\n"};
+for e,i in w(e.headers)do
+a[1],a[3]=e,i;
+t:write(o(a));
+end
+t:write("\r\n");
+if e.body then
+t:write(e.body);
+end
+end
+function r.onincoming(t,a)
+local e=n[t];
+if not e then
+d("warn","Received response from connection %s with no request attached!",l(t));
+return;
+end
+if a and e.reader then
+e:reader(a);
+end
+end
+function r.ondisconnect(t,a)
+local e=n[t];
+if e and e.conn then
+e:reader(nil,a or"closed");
+end
+n[t]=nil;
+end
+function r.ondetach(e)
+n[e]=nil;
+end
+local function o(e)
+if e.conn then
+e.conn=nil;
+e.handler:close()
+end
+end
+local function x(e,i,n)
 if not e.parser then
-if not a then return;end
-local function o(t)
-if e.callback then
-for a,t in i(t)do e[a]=t;end
-e.callback(t.body,t.code,e,t);
-e.callback=nil;
-end
-destroy_request(e);
-end
 local function a(t)
 if e.callback then
 e.callback(t or"connection-closed",0,e);
 e.callback=nil;
 end
-destroy_request(e);
+o(e);
+end
+if not i then
+a(n);
+return;
+end
+local function i(t)
+if e.callback then
+e.callback(t.body,t.code,t,e);
+e.callback=nil;
+end
+o(e);
 end
 local function t()
 return e;
 end
-e.parser=f(o,a,"client",t);
-end
-e.parser:feed(a);
-end
-local function f(e)r("error","Traceback[http]: %s: %s",l(e),v());end
-function request(e,t,s)
-local e=h.parse(e);
+e.parser=h(i,a,"client",t);
+end
+e.parser:feed(i);
+end
+local function q(e)d("error","Traceback[http]: %s",s(l(e),2));end
+local function j(e,t,...)
+if not t then
+d("error","Request '%s': error in callback: %s",e,l((...)));
+end
+return...;
+end
+local function f(o,a,t,h)
+local e=m.parse(a);
+e.url=a;
 if not(e and e.host)then
-s(nil,0,e);
+h("invalid-url",0,e);
 return nil,"invalid-url";
 end
 if not e.path then
 e.path="/";
 end
-local h,a,o;
-a={
-["Host"]=e.host;
+e.id=t and t.id or c(e);
+do
+local i={http=o,url=a,request=e,options=t,callback=h};
+local o=o.events.fire_event("pre-request",i);
+if o then
+return o;
+end
+e,a,t,h=i.request,i.url,i.options,i.callback;
+end
+local m,s,c;
+local f,i=e.host,e.port;
+local u=f;
+if(i=="80"and e.scheme=="http")
+or(i=="443"and e.scheme=="https")then
+i=nil;
+elseif i then
+u=u..":"..i;
+end
+s={
+["Host"]=u;
 ["User-Agent"]="Prosody XMPP Server";
 };
 if e.userinfo then
-a["Authorization"]="Basic "..u.b64(e.userinfo);
+s["Authorization"]="Basic "..z(e.userinfo);
 end
 if t then
 e.onlystatus=t.onlystatus;
-o=t.body;
-if o then
-h="POST";
-a["Content-Length"]=l(#o);
-a["Content-Type"]="application/x-www-form-urlencoded";
-end
-if t.method then h=t.method;end
+c=t.body;
+if c then
+m="POST";
+s["Content-Length"]=l(#c);
+s["Content-Type"]="application/x-www-form-urlencoded";
+end
+if t.method then m=t.method;end
 if t.headers then
-for t,e in i(t.headers)do
-a[t]=e;
-end
-end
-end
-e.method,e.headers,e.body=h,a,o;
-local o=e.scheme=="https";
-local i=d(e.port)or(o and 443 or 80);
-local t=c.tcp();
-t:settimeout(10);
-local h,a=t:connect(e.host,i);
-if not h and a~="timeout"then
-s(nil,0,e);
-return nil,a;
-end
-e.handler,e.conn=m.wrapclient(t,e.host,i,n,"*a",o and{mode="client",protocol="sslv23"});
+for e,t in w(t.headers)do
+s[e]=t;
+end
+end
+end
+d("debug","Making %s %s request '%s' to %s",e.scheme:upper(),m or"GET",e.id,(t and t.suppress_url and u)or a);
+e.method,e.headers,e.body=m,s,c;
+local s=e.scheme=="https";
+if s and not b then
+y("SSL not available, unable to contact https URL");
+end
+local l=i and v(i)or(s and 443 or 80);
+local i=false;
+if s then
+i=t and t.sslctx or{mode="client",protocol="sslv23",options={"no_sslv2","no_sslv3"}};
+end
+local i,t=k.addclient(f,l,r,"*a",i)
+if not i then
+o.events.fire_event("request-connection-error",{http=o,request=e,url=a,err=t});
+h(t,0,e);
+return nil,t;
+end
+e.handler,e.conn=i,t
 e.write=function(...)return e.handler:write(...);end
-e.callback=function(o,t,a,i)r("debug","Calling callback, status %s",t or"---");return y(2,w(function()return s(o,t,a,i)end,f));end
-e.reader=p;
+e.callback=function(i,t,n,s)
+do
+local e={http=o,url=a,request=e,response=n,content=i,code=t,callback=h};
+o.events.fire_event("response",e);
+i,t,n=e.content,e.code,e.response;
+end
+d("debug","Request '%s': Calling callback, status %s",e.id,t or"---");
+return j(e.id,p(function()return h(i,t,s,n)end,q));
+end
+e.reader=x;
 e.state="status";
-n.register_request(e.handler,e);
+n[e.handler]=e;
+o.events.fire_event("request",{http=o,request=e,url=a});
 return e;
 end
-function destroy_request(e)
-if e.conn then
-e.conn=nil;
-e.handler:close()
-n.ondisconnect(e.handler,"closed");
-end
-end
-_M.urlencode=urlencode;
-return _M;
+local function e(t)
+local e={
+options=t;
+request=f;
+new=t and function(a)
+return e(setmetatable(a,{__index=t}));
+end or e;
+events=g.new();
+request=f;
+};
+return e;
+end
+local t=e();
+return{
+request=function(e,a,o)
+return t:request(e,a,o);
+end;
+new=e;
+events=t.events;
+urlencode=u.urlencode;
+urldecode=u.urldecode;
+formencode=u.formencode;
+formdecode=u.formdecode;
+};
 end)
 package.preload['verse.bosh']=(function(...)
-local n=require"util.xmppstream".new;
-local r=require"util.stanza";
+local r=require"util.xmppstream".new;
+local h=require"util.stanza";
 require"net.httpclient_listener";
-local i=require"net.http";
+local o=require"net.http";
 local e=setmetatable({},{__index=verse.stream_mt});
 e.__index=e;
-local h="http://etherx.jabber.org/streams";
-local s="http://jabber.org/protocol/httpbind";
-local o=5;
+local s="http://etherx.jabber.org/streams";
+local n="http://jabber.org/protocol/httpbind";
+local i=5;
 function verse.new_bosh(a,t)
 local t={
 bosh_conn_pool={};
@@ -6685,7 +7932,7 @@
 end
 function e:send(e)
 self:debug("Putting into BOSH send buffer: %s",tostring(e));
-self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1]=r.clone(e);
+self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1]=h.clone(e);
 self:flush();
 end
 function e:flush()
@@ -6695,22 +7942,22 @@
 or#self.bosh_outgoing_buffer>0
 or self.bosh_need_restart)then
 self:debug("Flushing...");
-local t=self:_make_body();
-local e=self.bosh_outgoing_buffer;
-for o,a in ipairs(e)do
-t:add_child(a);
-e[o]=nil;
-end
-self:_make_request(t);
+local e=self:_make_body();
+local t=self.bosh_outgoing_buffer;
+for o,a in ipairs(t)do
+e:add_child(a);
+t[o]=nil;
+end
+self:_make_request(e);
 else
 self:debug("Decided not to flush.");
 end
 end
-function e:_make_request(a)
-local e,t=i.request(self.bosh_url,{body=tostring(a)},function(i,e,t)
+function e:_make_request(t)
+local e,t=o.request(self.bosh_url,{body=tostring(t)},function(o,e,a)
 if e~=0 then
 self.inactive_since=nil;
-return self:_handle_response(i,e,t);
+return self:_handle_response(o,e,a);
 end
 local e=os.time();
 if not self.inactive_since then
@@ -6719,17 +7966,17 @@
 return self:_disconnected();
 else
 self:debug("%d seconds left to reconnect, retrying in %d seconds...",
-self.bosh_max_inactivity-(e-self.inactive_since),o);
-end
-timer.add_task(o,function()
+self.bosh_max_inactivity-(e-self.inactive_since),i);
+end
+timer.add_task(i,function()
 self:debug("Retrying request...");
-for a,e in ipairs(self.bosh_waiting_requests)do
-if e==t then
-table.remove(self.bosh_waiting_requests,a);
+for e,t in ipairs(self.bosh_waiting_requests)do
+if t==a then
+table.remove(self.bosh_waiting_requests,e);
 break;
 end
 end
-self:_make_request(a);
+self:_make_request(t);
 end);
 end);
 if e then
@@ -6751,7 +7998,7 @@
 e.attr.from=self.jid;
 e.attr.to=self.host;
 e.attr.secure='true';
-i.request(self.bosh_url,{body=tostring(e)},function(e,t)
+o.request(self.bosh_url,{body=tostring(e)},function(e,t)
 if t==0 then
 return self:_disconnected();
 end
@@ -6771,7 +8018,7 @@
 self:_handle_response_payload(e);
 end);
 end
-function e:_handle_response(o,t,e)
+function e:_handle_response(t,a,e)
 if self.bosh_waiting_requests[1]~=e then
 self:warn("Server replied to request that wasn't the oldest");
 for t,a in ipairs(self.bosh_waiting_requests)do
@@ -6783,7 +8030,7 @@
 else
 table.remove(self.bosh_waiting_requests,1);
 end
-local e=self:_parse_response(o);
+local e=self:_parse_response(t);
 if e then
 self:_handle_response_payload(e);
 end
@@ -6793,7 +8040,7 @@
 local e=t.tags;
 for t=1,#e do
 local e=e[t];
-if e.attr.xmlns==h then
+if e.attr.xmlns==s then
 self:event("stream-"..e.name,e);
 elseif e.attr.xmlns then
 self:event("stream/"..e.attr.xmlns,e);
@@ -6819,14 +8066,14 @@
 return;
 end
 local t={notopen=true,stream=self};
-local a=n(t,a);
+local a=r(t,a);
 a:feed(e);
 return t.payload;
 end
 function e:_make_body()
 self.bosh_rid=self.bosh_rid+1;
 local e=verse.stanza("body",{
-xmlns=s;
+xmlns=n;
 content="text/xml; charset=utf-8";
 sid=self.bosh_sid;
 rid=self.bosh_rid;
@@ -6840,30 +8087,30 @@
 end)
 package.preload['verse.client']=(function(...)
 local t=require"verse";
-local i=t.stream_mt;
-local s=require"util.jid".split;
-local r=require"net.adns";
+local o=t.stream_mt;
+local h=require"util.jid".split;
+local d=require"net.adns";
 local e=require"lxp";
 local a=require"util.stanza";
 t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply=
 a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply;
-local h=require"util.xmppstream".new;
+local r=require"util.xmppstream".new;
 local n="http://etherx.jabber.org/streams";
-local function d(t,e)
+local function s(t,e)
 return t.priority<e.priority or(t.priority==e.priority and t.weight>e.weight);
 end
-local o={
+local i={
 stream_ns=n,
 stream_tag="stream",
 default_ns="jabber:client"};
-function o.streamopened(e,t)
+function i.streamopened(e,t)
 e.stream_id=t.id;
 if not e:event("opened",t)then
 e.notopen=nil;
 end
 return true;
 end
-function o.streamclosed(e)
+function i.streamclosed(e)
 e.notopen=true;
 if not e.closed then
 e:send("</stream:stream>");
@@ -6872,7 +8119,7 @@
 e:event("closed");
 return e:close("stream closed")
 end
-function o.handlestanza(t,e)
+function i.handlestanza(t,e)
 if e.attr.xmlns==n then
 return t:event("stream-"..e.name,e);
 elseif e.attr.xmlns then
@@ -6880,7 +8127,7 @@
 end
 return t:event("stanza",e);
 end
-function o.error(a,t,e)
+function i.error(a,t,e)
 if a:event(t,e)==nil then
 if e then
 local t=e:get_child(nil,"urn:ietf:params:xml:ns:xmpp-streams");
@@ -6891,18 +8138,18 @@
 end
 end
 end
-function i:reset()
+function o:reset()
 if self.stream then
 self.stream:reset();
 else
-self.stream=h(self,o);
+self.stream=r(self,i);
 end
 self.notopen=true;
 return true;
 end
-function i:connect_client(e,a)
+function o:connect_client(e,a)
 self.jid,self.password=e,a;
-self.username,self.host,self.resource=s(e);
+self.username,self.host,self.resource=h(e);
 self:add_plugin("tls");
 self:add_plugin("sasl");
 self:add_plugin("bind");
@@ -6910,18 +8157,18 @@
 function self.data(t,e)
 local a,t=self.stream:feed(e);
 if a then return;end
-self:debug("debug","Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," "));
+self:debug("Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," "));
 self:close("xml-not-well-formed");
 end
 self:hook("connected",function()self:reopen();end);
 self:hook("incoming-raw",function(e)return self.data(self.conn,e);end);
 self.curr_id=0;
 self.tracked_iqs={};
-self:hook("stanza",function(e)
-local t,a=e.attr.id,e.attr.type;
-if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then
-self.tracked_iqs[t](e);
-self.tracked_iqs[t]=nil;
+self:hook("stanza",function(t)
+local e,a=t.attr.id,t.attr.type;
+if e and t.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[e]then
+self.tracked_iqs[e](t);
+self.tracked_iqs[e]=nil;
 return true;
 end
 end);
@@ -6971,22 +8218,22 @@
 return self:close(e);
 end
 end
-local function a()
+local function t()
 self:connect(self.connect_host or self.host,self.connect_port or 5222);
 end
 if not(self.connect_host or self.connect_port)then
-r.lookup(function(t)
-if t then
+d.lookup(function(a)
+if a then
 local e={};
 self.srv_hosts=e;
-for a,t in ipairs(t)do
+for a,t in ipairs(a)do
 table.insert(e,t.srv);
 end
-table.sort(e,d);
-local t=e[1];
+table.sort(e,s);
+local a=e[1];
 self.srv_choice=1;
-if t then
-self.connect_host,self.connect_port=t.target,t.port;
+if a then
+self.connect_host,self.connect_port=a.target,a.port;
 self:debug("Best record found, will connect to %s:%d",self.connect_host or self.host,self.connect_port or 5222);
 end
 self:hook("disconnected",function()
@@ -6994,7 +8241,7 @@
 self.srv_choice=self.srv_choice+1;
 local e=e[self.srv_choice];
 self.connect_host,self.connect_port=e.target,e.port;
-a();
+t();
 return true;
 end
 end,1e3);
@@ -7002,37 +8249,37 @@
 self.srv_hosts=nil;
 end,1e3);
 end
-a();
+t();
 end,"_xmpp-client._tcp."..(self.host)..".","SRV");
 else
-a();
-end
-end
-function i:reopen()
+t();
+end
+end
+function o:reopen()
 self:reset();
 self:send(a.stanza("stream:stream",{to=self.host,["xmlns:stream"]='http://etherx.jabber.org/streams',
 xmlns="jabber:client",version="1.0"}):top_tag());
 end
-function i:send_iq(e,a)
+function o:send_iq(e,a)
 local t=self:new_id();
 self.tracked_iqs[t]=a;
 e.attr.id=t;
 self:send(e);
 end
-function i:new_id()
+function o:new_id()
 self.curr_id=self.curr_id+1;
 return tostring(self.curr_id);
 end
 end)
 package.preload['verse.component']=(function(...)
-local t=require"verse";
-local a=t.stream_mt;
+local a=require"verse";
+local o=a.stream_mt;
 local d=require"util.jid".split;
 local e=require"lxp";
-local o=require"util.stanza";
+local t=require"util.stanza";
 local r=require"util.sha1".sha1;
-t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply=
-o.message,o.presence,o.iq,o.stanza,o.reply,o.error_reply;
+a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply=
+t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply;
 local h=require"util.xmppstream".new;
 local s="http://etherx.jabber.org/streams";
 local i="jabber:component:accept";
@@ -7058,7 +8305,7 @@
 end
 return t:event("stanza",e);
 end
-function a:reset()
+function o:reset()
 if self.stream then
 self.stream:reset();
 else
@@ -7067,14 +8314,14 @@
 self.notopen=true;
 return true;
 end
-function a:connect_component(e,n)
+function o:connect_component(e,n)
 self.jid,self.password=e,n;
 self.username,self.host,self.resource=d(e);
 function self.data(t,e)
-local o,t=self.stream:feed(e);
-if o then return;end
-a:debug("debug","Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," "));
-a:close("xml-not-well-formed");
+local t,a=self.stream:feed(e);
+if t then return;end
+o:debug("Received invalid XML (%s) %d bytes: %s",tostring(a),#e,e:sub(1,300):gsub("[\r\n]+"," "));
+o:close("xml-not-well-formed");
 end
 self:hook("incoming-raw",function(e)return self.data(self.conn,e);end);
 self.curr_id=0;
@@ -7088,30 +8335,30 @@
 end
 end);
 self:hook("stanza",function(e)
-local a;
+local t;
 if e.attr.xmlns==nil or e.attr.xmlns=="jabber:client"then
 if e.name=="iq"and(e.attr.type=="get"or e.attr.type=="set")then
 local o=e.tags[1]and e.tags[1].attr.xmlns;
 if o then
-a=self:event("iq/"..o,e);
-if not a then
-a=self:event("iq",e);
-end
-end
-if a==nil then
-self:send(t.error_reply(e,"cancel","service-unavailable"));
+t=self:event("iq/"..o,e);
+if not t then
+t=self:event("iq",e);
+end
+end
+if t==nil then
+self:send(a.error_reply(e,"cancel","service-unavailable"));
 return true;
 end
 else
-a=self:event(e.name,e);
-end
-end
-return a;
+t=self:event(e.name,e);
+end
+end
+return t;
 end,-1);
 self:hook("opened",function(e)
 print(self.jid,self.stream_id,e.id);
 local e=r(self.stream_id..n,true);
-self:send(o.stanza("handshake",{xmlns=i}):text(e));
+self:send(t.stanza("handshake",{xmlns=i}):text(e));
 self:hook("stream/"..i,function(e)
 if e.name=="handshake"then
 self:event("authentication-success");
@@ -7125,48 +8372,48 @@
 self:connect(self.connect_host or self.host,self.connect_port or 5347);
 self:reopen();
 end
-function a:reopen()
+function o:reopen()
 self:reset();
-self:send(o.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams',
+self:send(t.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams',
 xmlns=i,version="1.0"}):top_tag());
 end
-function a:close(t)
+function o:close(e)
 if not self.notopen then
 self:send("</stream:stream>");
 end
-local e=self.conn.disconnect();
+local t=self.conn.disconnect();
 self.conn:close();
-e(conn,t);
-end
-function a:send_iq(t,a)
+t(conn,e);
+end
+function o:send_iq(t,a)
 local e=self:new_id();
 self.tracked_iqs[e]=a;
 t.attr.id=e;
 self:send(t);
 end
-function a:new_id()
+function o:new_id()
 self.curr_id=self.curr_id+1;
 return tostring(self.curr_id);
 end
 end)
 pcall(require,"luarocks.require");
-local s=require"socket";
+local h=require"socket";
 pcall(require,"ssl");
 local a=require"net.server";
-local n=require"util.events";
+local s=require"util.events";
 local o=require"util.logger";
-module("verse",package.seeall);
-local e=_M;
-_M.server=a;
+local t={};
+local e=t;
+t.server=a;
 local t={};
 t.__index=t;
-stream_mt=t;
+e.stream_mt=t;
 e.plugins={};
 function e.init(...)
 for e=1,select("#",...)do
-local t,a=pcall(require,"verse."..select(e,...));
-if not t then
-error("Verse connection module not found: verse."..select(e,...).."\n"..a);
+local a,t=pcall(require,"verse."..select(e,...));
+if not a then
+error("Verse connection module not found: verse."..select(e,...)..t);
 end
 end
 return e;
@@ -7177,7 +8424,7 @@
 i=i+1;
 t.id=tostring(i);
 t.logger=o or e.new_logger("stream"..t.id);
-t.events=n.new();
+t.events=s.new();
 t.plugins={};
 t.verse=e;
 return t;
@@ -7186,32 +8433,32 @@
 e.logger=o.init;
 e.new_logger=o.init;
 e.log=e.logger("verse");
-local function i(a,...)
-local e,o,t=0,{...},select('#',...);
-return(a:gsub("%%(.)",function(a)if e<=t then e=e+1;return tostring(o[e]);end end));
+local function n(a,...)
+local e,t,o=0,{...},select('#',...);
+return(a:gsub("%%(.)",function(a)if e<=o then e=e+1;return tostring(t[e]);end end));
 end
 function e.set_log_handler(e,t)
 t=t or{"debug","info","warn","error"};
 o.reset();
 if io.type(e)=="file"then
-local t=e;
-function e(a,e,o)
-t:write(a,"\t",e,"\t",o,"\n");
+local o=e;
+function e(t,a,e)
+o:write(t,"\t",a,"\t",e,"\n");
 end
 end
 if e then
-local function a(t,a,o,...)
-return e(t,a,i(o,...));
+local function i(o,a,t,...)
+return e(o,a,n(t,...));
 end
 for t,e in ipairs(t)do
-o.add_level_sink(e,a);
-end
-end
-end
-function _default_log_handler(o,a,t)
+o.add_level_sink(e,i);
+end
+end
+end
+function e._default_log_handler(o,a,t)
 return io.stderr:write(o,"\t",a,"\t",t,"\n");
 end
-e.set_log_handler(_default_log_handler,{"error"});
+e.set_log_handler(e._default_log_handler,{"error"});
 local function o(t)
 e.log("error","Error: %s",t);
 e.log("error","Traceback: %s",debug.traceback());
@@ -7228,32 +8475,33 @@
 function e.quit()
 return a.setquitting(true);
 end
-function t:listen(t,e)
-t=t or"localhost";
-e=e or 0;
-local a,o=a.addserver(t,e,new_listener(self,"server"),"*a");
-if a then
-self:debug("Bound to %s:%s",t,e);
-self.server=a;
-end
-return a,o;
-end
-function t:connect(t,o)
+function t:listen(t,o)
 t=t or"localhost";
-o=tonumber(o)or 5222;
-local i=s.tcp()
-i:settimeout(0);
-local n,e=i:connect(t,o);
-if not n and e~="timeout"then
-self:warn("connect() to %s:%d failed: %s",t,o,e);
-return self:event("disconnected",{reason=e})or false,e;
-end
-local t=a.wrapclient(i,t,o,new_listener(self),"*a");
-if not t then
-self:warn("connection initialisation failed: %s",e);
-return self:event("disconnected",{reason=e})or false,e;
-end
-self:set_conn(t);
+o=o or 0;
+local e,a=a.addserver(t,o,e.new_listener(self,"server"),"*a");
+if e then
+self:debug("Bound to %s:%s",t,o);
+self.server=e;
+end
+return e,a;
+end
+function t:connect(o,i)
+o=o or"localhost";
+i=tonumber(i)or 5222;
+local n=h.tcp()
+n:settimeout(0);
+n:setoption("keepalive",true);
+local s,t=n:connect(o,i);
+if not s and t~="timeout"then
+self:warn("connect() to %s:%d failed: %s",o,i,t);
+return self:event("disconnected",{reason=t})or false,t;
+end
+local e=a.wrapclient(n,o,i,e.new_listener(self),"*a");
+if not e then
+self:warn("connection initialisation failed: %s",t);
+return self:event("disconnected",{reason=t})or false,t;
+end
+self:set_conn(e);
 return true;
 end
 function t:set_conn(t)
@@ -7297,7 +8545,7 @@
 return self.events.remove_handler(t,e);
 end
 function e.eventable(e)
-e.events=n.new();
+e.events=s.new();
 e.hook,e.unhook=t.hook,t.unhook;
 local t=e.events.fire_event;
 function e:event(e,...)
@@ -7318,14 +8566,14 @@
 end
 return self;
 end
-function new_listener(t)
+function e.new_listener(t)
 local a={};
-function a.onconnect(a)
+function a.onconnect(o)
 if t.server then
-local e=e.new();
-a:setlistener(new_listener(e));
-e:set_conn(a);
-t:event("connected",{client=e});
+local a=e.new();
+o:setlistener(e.new_listener(a));
+a:set_conn(o);
+t:event("connected",{client=a});
 else
 t.connected=true;
 t:event("connected");
@@ -7334,10 +8582,10 @@
 function a.onincoming(a,e)
 t:event("incoming-raw",e);
 end
-function a.ondisconnect(a,e)
-if a~=t.conn then return end
+function a.ondisconnect(e,a)
+if e~=t.conn then return end
 t.connected=false;
-t:event("disconnected",{reason=e});
+t:event("disconnected",{reason=a});
 end
 function a.ondrain(e)
 t:event("drained");

mercurial