diff -r bd7888c3745a -r 98b4cf14960b verse.lua --- 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={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; -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={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; +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,""); -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,""); +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"";end +local e={}; +for t in t:gmatch("[^;]+")do +s(e,p[o(t)]); +end +return""; +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"; 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#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=""; -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=""; +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""); -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 -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(""); +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 ..."); -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.category0 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=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 +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.prioritye.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(""); @@ -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(""); 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");