diff -r 984b3ba0ea99 -r 9e0e56393978 verse.lua --- a/verse.lua Mon Sep 10 09:50:42 2018 +0100 +++ b/verse.lua Mon Sep 10 09:51:01 2018 +0100 @@ -1,217 +1,118 @@ package.preload['util.encodings']=(function(...) +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local function e() error("Function not implemented"); end local e=require"mime"; -return{ +a"encodings" stringprep={}; base64={encode=e.b64,decode=e.unb64}; -}; +return _M; end) package.preload['util.hashes']=(function(...) -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 r=string.len -local o=string.char -local j=string.byte -local q=string.sub -local h=math.floor -local t=require"bit" -local g=t.bnot -local e=t.band -local y=t.bor -local n=t.bxor -local a=t.lshift -local i=t.rshift -local m,d,c,l,u -local function p(e,t) -return a(e,t)+i(e,32-t) -end -local function s(i) -local t,a -local t="" -for n=1,8 do -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 k(t) -local i,a -local n="" -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=o(e(i,255))..n -i=h(i/256) -end -return t..n -end -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 -a[e]=0 -for t=1,4 do -a[e]=a[e]*256+j(f,e*4+t) -end -end -for e=16,79 do -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 -return(e:gsub("..",function(e) -return string.char(tonumber(e,16)); -end)); -end -end -_G.sha1={sha1=t}; -return _G.sha1; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local function t(t,e) +error("Hash method "..e.." not available",2); +end +local e=setmetatable({},{__index=t}); +local function t(e,o) +local e,a=pcall(require,e); +if e then o(a);end +end +t("bgcrypto.md5",function(t) +e.md5=t.digest; +e.hmac_md5=t.hmac.digest; +end); +t("bgcrypto.sha1",function(t) +e.sha1=t.digest; +e.hmac_sha1=t.hmac.digest; +e.scram_Hi_sha1=function(e,a,o)return t.pbkdf2(e,a,o,20);end; +end); +t("bgcrypto.sha256",function(t) +e.sha256=t.digest; +e.hmac_sha256=t.hmac.digest; +end); +t("bgcrypto.sha512",function(t) +e.sha512=t.digest; +e.hmac_sha512=t.hmac.digest; +end); +t("sha1",function(t) +e.sha1=function(e,a) +if a then +return t.sha1(e); +else +return(t.binary(e)); +end +end; +end); +return e; end) package.preload['lib.adhoc']=(function(...) -local s,r=require"util.stanza",require"util.uuid"; +local _ENV=_ENV; +local function r(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local s,i=require"util.stanza",require"util.uuid"; local e="http://jabber.org/protocol/commands"; -local i={} +local n={} local h={}; -local function n(o,i,t,a) -local e=s.stanza("command",{xmlns=e,node=o.node,status=i}); +local function o(i,o,t,a) +local e=s.stanza("command",{xmlns=e,node=i.node,status=o}); if t then e.attr.sessionid=t;end if a then e.attr.action=a;end return e; end -function h.new(o,a,t,e) -return{name=o,node=a,handler=t,cmdtag=n,permission=(e or"user")}; +function h.new(a,i,e,t) +return{name=a,node=i,handler=e,cmdtag=o,permission=(t or"user")}; end function h.handle_cmd(o,h,t) -local e=t.tags[1].attr.sessionid or r.generate(); +local e=t.tags[1].attr.sessionid or i.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 a,i=o:handler(a,n[e]); +n[e]=i; +local i=s.reply(t); local t; if a.status=="completed"then -i[e]=nil; +n[e]=nil; t=o:cmdtag("completed",e); elseif a.status=="canceled"then -i[e]=nil; +n[e]=nil; t=o:cmdtag("canceled",e); elseif a.status=="error"then -i[e]=nil; -n=s.error_reply(n,a.error.type,a.error.condition,a.error.message); -h.send(n); +n[e]=nil; +i=s.error_reply(i,a.error.type,a.error.condition,a.error.message); +h.send(i); return true; else t=o:cmdtag("executing",e); @@ -229,7 +130,7 @@ if(e=="prev")or(e=="next")or(e=="complete")then a:tag(e):up(); else -module:log("error",'Command "'..o.name.. +r:log("error",'Command "'..o.name.. '" at node "'..o.node..'" provided an invalid action "'..e..'"'); end end @@ -242,57 +143,66 @@ t:add_child(e); end end -n:add_child(t); -h.send(n); +i:add_child(t); +h.send(i); return true; end return h; end) package.preload['util.stanza']=(function(...) +local _ENV=_ENV; +local function h(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=table.insert; -local d=table.remove; -local y=table.concat; -local h=string.format; -local l=string.match; -local f=tostring; +local s=table.remove; +local p=table.concat; +local r=string.format; +local u=string.match; +local w=tostring; local m=setmetatable; -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 i=ipairs; +local o=type; +local y=string.gsub; +local l=string.sub; +local f=string.find; local e=os; -local u=not e.getenv("WINDIR"); -local r,a; -if u then +local c=not e.getenv("WINDIR"); +local d,a; +if c then local t,e=pcall(require,"util.termcolours"); if t then -r,a=e.getstyle,e.getstring; -else -u=nil; +d,a=e.getstyle,e.getstring; +else +c=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={}}; +h"stanza" +stanza_mt={__type="stanza"}; +stanza_mt.__index=stanza_mt; +local e=stanza_mt; +function stanza(a,t) +local t={name=a,attr=t or{},tags={}}; return m(t,e); end -local function k(t) -return p(t)==e; -end +local h=stanza; 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(e,a) -local a=s(e,a); +function e:tag(a,e) +local a=h(a,e); local e=self.last_add; if not e then e={};self.last_add=e;end (e[#e]or self):add_direct_child(a); @@ -306,7 +216,7 @@ end function e:up() local e=self.last_add; -if e then d(e);end +if e then s(e);end return self; end function e:reset() @@ -314,7 +224,7 @@ return self; end function e:add_direct_child(e) -if i(e)=="table"then +if o(e)=="table"then t(self.tags,e); end t(self,e); @@ -325,7 +235,7 @@ return self; end function e:get_child(t,a) -for o,e in o(self.tags)do +for o,e in i(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 @@ -333,20 +243,20 @@ end end end -function e:get_child_text(t,e) -local e=self:get_child(t,e); +function e:get_child_text(e,t) +local e=self:get_child(e,t); if e then return e:get_text(); end return nil; end function e:child_with_name(t) -for a,e in o(self.tags)do +for a,e in i(self.tags)do if e.name==t then return e;end end end function e:child_with_ns(t) -for a,e in o(self.tags)do +for a,e in i(self.tags)do if e.attr.xmlns==t then return e;end end end @@ -357,15 +267,15 @@ return t[e]; end,self,e; end -function e:childtags(o,i) +function e:childtags(i,o) local e=self.tags; local a,t=1,#e; return function() 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 +if(not i or e.name==i) +and((not o and self.attr.xmlns==e.attr.xmlns) +or e.attr.xmlns==o)then a=t+1; return e; end @@ -373,27 +283,27 @@ end; end function e:maptags(i) -local o,e=self.tags,1; +local o,t=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]); +local e=1; +while t<=a and a>0 do +if self[e]==o[t]then +local i=i(self[e]); if i==nil then -d(self,t); -d(o,e); +s(self,e); +s(o,t); n=n-1; a=a-1; +e=e-1; t=t-1; -e=e-1; -else -self[t]=i; -o[e]=i; +else +self[e]=i; +o[t]=i; +end +t=t+1; end e=e+1; end -t=t+1; -end return self; end function e:find(a) @@ -401,13 +311,13 @@ local s=#a+1; repeat local o,t,n; -local i=w(a,e,e); +local i=l(a,e,e); if i=="@"then -return self.attr[w(a,e+1)]; +return self.attr[l(a,e+1)]; elseif i=="{"then -o,e=l(a,"^([^}]+)}()",e+1); -end -t,n,e=l(a,"^([^@/#]*)([/#]?)()",e); +o,e=u(a,"^([^}]+)}()",e+1); +end +t,n,e=u(a,"^([^@/#]*)([/#]?)()",e); t=t~=""and t or nil; if e==s then if n=="#"then @@ -418,17 +328,21 @@ 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 l +do +local e={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; +function l(t)return(y(t,"['&<>\"]",e));end +_M.xml_escape=l; +end +local function y(o,e,h,a,r) local i=0; local s=o.name t(e,"<"..s); for o,n in n(o.attr)do -if c(o,"\1",1,true)then -local o,s=l(o,"^([^\1]*)\1?(.*)$"); +if f(o,"\1",1,true)then +local s,o=u(o,"^([^\1]*)\1?(.*)$"); i=i+1; -t(e," xmlns:ns"..i.."='"..a(o).."' ".."ns"..i..":"..s.."='"..a(n).."'"); +t(e," xmlns:ns"..i.."='"..a(s).."' ".."ns"..i..":"..o.."='"..a(n).."'"); elseif not(o=="xmlns"and n==r)then t(e," "..o.."='"..a(n).."'"); end @@ -451,280 +365,288 @@ end function e.__tostring(t) local 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); +y(t,e,y,l,nil); +return p(e); +end +function e.top_tag(t) +local e=""; +if t.attr then +for t,a in n(t.attr)do if o(t)=="string"then e=e..r(" %s='%s'",t,l(w(a)));end end +end +return r("<%s%s>",t.name,e); end function e.get_text(e) if#e.tags==0 then -return y(e); +return p(e); end end function e.get_error(e) -local i,t,a; +local o,a,t; local e=e:get_child("error"); if not e then return nil,nil,nil; end -i=e.attr.type; -for o,e in o(e.tags)do +o=e.attr.type; +for o,e in i(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 +if not t and e.name=="text"then +t=e:get_text(); +elseif not a then +a=e.name; +end +if a and t then break; end end end -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) +return o,a or"undefined-condition",t; +end +do +local e=0; +function new_id() +e=e+1; +return"lx"..e; +end +end +function preserialize(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)); +for i,e in i(e)do +if o(e)=="table"then +t(a,preserialize(e)); else t(a,e); end end return a; end -local function p(a) +function deserialize(a) if a then local s=a.attr; for e=1,#s do s[e]=nil;end local h={}; 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]; +if f(e,"|",1,true)and not f(e,"\1",1,true)then +local t,a=u(e,"^([^|]+)|(.+)$"); +h[t.."\1"..a]=s[e]; s[e]=nil; end end -for e,t in n(h)do -s[e]=t; +for t,e in n(h)do +s[t]=e; end m(a,e); -for t,e in o(a)do -if i(e)=="table"then -p(e); +for t,e in i(a)do +if o(e)=="table"then +deserialize(e); end end if not a.tags then -local e={}; -for n,o in o(a)do -if i(o)=="table"then -t(e,o); -end -end -a.tags=e; +local n={}; +for i,e in i(a)do +if o(e)=="table"then +t(n,e); +end +end +a.tags=n; end end return a; end -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}; +local function s(a) +local i,o={},{}; +for t,e in n(a.attr)do i[t]=e;end +local i={name=a.name,attr=i,tags=o}; for e=1,#a do local e=a[e]; if e.name then -e=l(e); -t(i,e); -end +e=s(e); t(o,e); end -return m(o,e); -end -local function b(t,e) +t(i,e); +end +return m(i,e); +end +clone=s; +function message(t,e) if not e then -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); +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=v}; +function error_reply(e,o,i,a) +local e=reply(e); e.attr.type="error"; e:tag("error",{type=o}) -:tag(a,c):up(); -if t then e:tag("text",c):text(t):up();end -return e; -end -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(); +:tag(i,t):up(); +if(a)then e:tag("text",t):text(a):up();end +return e; +end +end +function presence(e) +return h("presence",e); +end +if c then +local s=d("yellow"); +local h=d("red"); +local u=d("red"); +local t=d("magenta"); +local h=" "..a(s,"%s")..a(t,"=")..a(h,"'%s'"); +local s=a(t,"<")..a(u,"%s").."%s"..a(t,">"); +local d=s.."%s"..a(t,""); +function e.pretty_print(e) +local t=""; +for a,e in i(e)do +if o(e)=="string"then +t=t..l(e); +else +t=t..e:pretty_print(); end end local a=""; -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); +if e.attr then +for e,t in n(e.attr)do if o(e)=="string"then a=a..r(h,e,w(t));end end +end +return r(d,e.name,a,t,e.name); +end +function e.pretty_top_tag(e) +local t=""; +if e.attr then +for e,a in n(e.attr)do if o(e)=="string"then t=t..r(h,e,w(a));end end +end +return r(s,e.name,t); end else e.pretty_print=e.__tostring; e.pretty_top_tag=e.top_tag; end -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; -}; +return _M; end) package.preload['util.timer']=(function(...) +local _ENV=_ENV; +local function o(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end 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 h=math.min +local u=math.huge +local i=require"socket".gettime; +local d=table.insert; +local l=pairs; +local r=type; +local s={}; local e={}; -local t=nil; +o"timer" local t; 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); +function t(o,n) +local i=i(); +o=o+i; +if o>=i then +d(e,{o,n}); +else +local e=n(i); +if e and r(e)=="number"then +return t(e,n); end end end a._addtimer(function() -local a=n(); +local a=i(); if#e>0 then -for a,t in r(e)do -h(i,t); +for a,t in l(e)do +d(s,t); end e={}; end -local e=l; -for h,o in r(i)do -local o,n=o[1],o[2]; +local e=u; +for n,o in l(s)do +local o,i=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); +s[n]=nil; +local a=i(a); +if r(a)=="number"then +t(a,i); +e=h(e,a); +end +else +e=h(e,o-a); end end return e; end); else local e=a.event; -local o=a.event_base; -local i=(e.core and e.core.LEAVE)or-1; -function t(a,e) +local n=a.event_base; +local a=(e.core and e.core.LEAVE)or-1; +function t(o,e) local t; -t=o:addevent(nil,0,function() -local e=e(n()); +t=n:addevent(nil,0,function() +local e=e(i()); if e then return 0,e; elseif t then -return i; -end -end -,a); -end -end -return{ +return a; +end +end +,o); +end +end add_task=t; -}; +return _M; end) package.preload['util.termcolours']=(function(...) -local h,s=table.concat,table.insert; -local a,r=string.char,string.format; -local o=tonumber; -local d=ipairs; +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local n,i=table.concat,table.insert; +local t,s=string.char,string.format; +local h=tonumber; +local c=ipairs; local l=io.write; -local i=math.floor; -local u=type; -local m=setmetatable; -local f=pairs; -local t; +local e; if os.getenv("WINDIR")then -t=require"util.windows"; -end -local n=t and t.get_consolecolor and t.get_consolecolor(); -local e=nil; -local e={ +e=require"util.windows"; +end +local o=e and e.get_consolecolor and e.get_consolecolor(); +a"termcolours" +local u={ 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 c={ -["0"]=n, +local d={ +["0"]=o, ["1"]=7+8, ["1;33"]=2+4+8, ["1;31"]=4+8 } -local p={ +local r={ [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", @@ -732,127 +654,123 @@ [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) +local a=t(27).."[%sm%s"..t(27).."[0m"; +function getstring(e,t) if e then -return r(a,e,t); +return s(a,e,t); else return t; end end -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,";"); +function getstyle(...) +local e,t={...},{}; +for a,e in c(e)do +e=u[e]; +if e then +i(t,e); +end +end +return n(t,";"); end local a="0"; -local function i(e) +function setstyle(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 -t.set_consolecolor(c[e]or n); -a=e; -end -end -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) +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 +local function a(e) +if e=="0"then return"";end +local t={}; +for e in e:gmatch("[^;]+")do +i(t,r[h(e)]); +end +return""; +end +function tohtml(e) return e:gsub("\027%[(.-)m",a); end -return{ -getstring=y; -getstyle=r; -setstyle=i; -tohtml=e; -}; +return _M; end) package.preload['util.uuid']=(function(...) -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 _ENV=_ENV; +local function o(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local n=tostring; +local e=os.time; +local a=os.clock; +local i=require"util.hashes".sha1; +o"uuid" +local t=0; local function o() +local e=e(); +if t>=e then e=t+1;end +t=e; +return e; +end +local function t(e) +return i(e..a()..n({}),true); +end +local e=t(o()); +local function a(a) +e=t(e..a); +end +local function t(t) +if#e"; end return e; end -local y={ +local w={ LOC=e.LOC_tostring; MX=function(e) return a.format('%2i %s',e.pref,e.mx); @@ -972,33 +890,40 @@ }; local x={}; function x.__tostring(e) -local t=(y[e.type]or j)(e); +local t=(w[e.type]or j)(e); return a.format('%2s %-5s %6i %-28s %s',e.class,e.type,e.ttl,e.name,t); end local j={}; function j.__tostring(t) local e={}; -for a,t in c(t)do -h(e,b(t)..'\n'); -end -return n.concat(e); -end -local y={}; -function y.__tostring(e) -local a=i.gettime(); +for a,t in m(t)do +n(e,p(t)..'\n'); +end +return i.concat(e); +end +local w={}; +function w.__tostring(e) +local a=s.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); +y(e,a); +n(t,p(e)); +end +end +end +return i.concat(t); +end +function e:new() +local t={active={},cache={},unsorted={}}; +r(t,e); +r(t.cache,w); +r(t.unsorted,{__mode='kv'}); +return t; end function t.random(...) -v.randomseed(v.floor(1e4*i.gettime())%2147483648); +v.randomseed(v.floor(1e4*s.gettime())%2147483648); t.random=v.random; return t.random(...); end @@ -1018,27 +943,27 @@ e.nscount=e.nscount or 0; e.arcount=e.arcount or 0; local t=a.char( -f(e.id),e.id%256, +c(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, -f(e.qdcount),e.qdcount%256, -f(e.ancount),e.ancount%256, -f(e.nscount),e.nscount%256, -f(e.arcount),e.arcount%256 +c(e.qdcount),e.qdcount%256, +c(e.ancount),e.ancount%256, +c(e.nscount),e.nscount%256, +c(e.arcount),e.arcount%256 ); return t,e.id; end -local function f(t) +local function c(t) local e={}; for t in a.gmatch(t,'[^.]+')do -h(e,a.char(a.len(t))); -h(e,t); -end -h(e,a.char(0)); -return n.concat(e); -end -local function T(o,a,e) -o=f(o); +n(e,a.char(a.len(t))); +n(e,t); +end +n(e,a.char(0)); +return i.concat(e); +end +local function z(o,a,e) +o=c(o); a=t.typecode[a or'a']; e=t.classcode[e or'in']; return o..a..e; @@ -1058,8 +983,8 @@ return 256*t+e; end function e:dword() -local e,t,o,a=self:byte(4); -return 16777216*e+65536*t+256*o+a; +local o,t,a,e=self:byte(4); +return 16777216*o+65536*t+256*a+e; end function e:sub(e) e=e or 1; @@ -1088,24 +1013,24 @@ return e; end function e:name() -local t,a=nil,0; +local a,t=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 q('dns error: 20 pointers');end; +t=t+1; +if t>=20 then q('dns error: 20 pointers');end; local e=((e-192)*256)+self:byte(); -t=t or self.offset; +a=a or self.offset; self.offset=e+1; else -h(o,self:sub(e)..'.'); +n(o,self:sub(e)..'.'); end e=self:byte(); end -self.offset=t or self.offset; -return n.concat(o); +self.offset=a or self.offset; +return i.concat(o); end function e:question() local e={}; @@ -1114,26 +1039,26 @@ e.class=t.class[self:word()]; return e; end -function e:A(n) -local i,o,e,t=self:byte(4); -n.a=a.format('%i.%i.%i.%i',i,o,e,t); +function e:A(e) +local o,t,n,i=self:byte(4); +e.a=a.format('%i.%i.%i.%i',o,t,n,i); end function e:AAAA(a) local e={}; for t=1,a.rdlength,2 do 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"); +i.insert(e,("%02x%02x"):format(t,a)); +end +e=i.concat(e,":"):gsub("%f[%x]0+(%x)","%1"); local t={}; for e in e:gmatch(":[0:]+:")do -n.insert(t,e) +i.insert(t,e) end if#t==0 then a.aaaa=e; return elseif#t>1 then -n.sort(t,function(e,t)return#e>#t end); +i.sort(t,function(t,e)return#t>#e end); end a.aaaa=e:gsub(t[1],"::",1):gsub("^0::","::"):gsub("::0$","::"); end @@ -1160,28 +1085,28 @@ e.loc.altitude=self:dword(); end end -local function f(e,i,t) +local function c(e,i,t) e=e-2147483648; if e<0 then i=t;e=-e;end -local n,t,o; -o=e%6e4; -e=(e-o)/6e4; -t=e%60; -n=(e-t)/60; -return a.format('%3d %2d %2.3f %s',n,t,o/1e3,i); +local n,o,t; +t=e%6e4; +e=(e-t)/6e4; +o=e%60; +n=(e-o)/60; +return a.format('%3d %2d %2.3f %s',n,o,t/1e3,i); end function e.LOC_tostring(e) local t={}; -h(t,a.format( +n(t,a.format( '%s %s %.2fm %.2fm %.2fm %.2fm', -f(e.loc.latitude,'N','S'), -f(e.loc.longitude,'E','W'), +c(e.loc.latitude,'N','S'), +c(e.loc.longitude,'E','W'), (e.loc.altitude-1e7)/100, e.loc.size/100, e.loc.horiz_pre/100, e.loc.vert_pre/100 )); -return n.concat(t); +return i.concat(t); end function e:NS(e) e.ns=self:name(); @@ -1203,7 +1128,7 @@ end function e:rr() local e={}; -u(e,x); +r(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; @@ -1223,7 +1148,7 @@ end function e:rrs(t) local e={}; -for t=1,t do h(e,self:rr());end +for t=1,t do n(e,self:rr());end return e; end function e:decode(t,o) @@ -1234,7 +1159,7 @@ t.question={}; local i=self.offset; for e=1,t.header.qdcount do -h(t.question,self:question()); +n(t.question,self:question()); end t.question.raw=a.sub(self.packet,i,self.offset-1); if not o then @@ -1251,16 +1176,16 @@ e.delays={1,3}; function e:addnameserver(e) self.server=self.server or{}; -h(self.server,e); +n(self.server,e); end function e:setnameserver(e) self.server={}; self:addnameserver(e); end function e:adddefaultnameservers() -if E then -if w and w.get_nameservers then -for t,e in c(w.get_nameservers())do +if _ then +if b and b.get_nameservers then +for t,e in m(b.get_nameservers())do self:addnameserver(e); end end @@ -1269,16 +1194,15 @@ self:addnameserver("208.67.220.220"); end else -local e=_.open("/etc/resolv.conf"); +local e=E.open("/etc/resolv.conf"); if e then for e in e:lines()do e=e:gsub("#.*$","") -:match('^%s*nameserver%s+([%x:%.]*%%?%S*)%s*$'); +:match('^%s*nameserver%s+(.*)%s*$'); if e then -local e=z(e); -if e then -self:addnameserver(e.addr); -end +e:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]",function(e) +self:addnameserver(e) +end); end end end @@ -1287,29 +1211,24 @@ end end end -function e:getsocket(a) +function e:getsocket(o) self.socket=self.socket or{}; self.socketset=self.socketset or{}; -local e=self.socket[a]; +local e=self.socket[o]; if e then return e;end -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 +local a,t; +e,t=s.udp(); if e and self.socket_wrapper then e,t=self.socket_wrapper(e,self);end if not e then return nil,t; end e:settimeout(0); -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 +self.socket[o]=e; +self.socketset[e]=o; +a,t=e:setsockname('*',0); +if not a then return self:servfail(e,t);end +a,t=e:setpeername(self.server[o],53); +if not a then return self:servfail(e,t);end return e; end function e:voidsocket(e) @@ -1326,100 +1245,100 @@ self.socket_wrapper=e; end function e:closeall() -for t,e in c(self.socket)do +for t,e in m(self.socket)do self.socket[t]=nil; self.socketset[e]=nil; e:close(); end end function e:remember(e,t) -local i,o,a=g(e.name,e.type,e.class); +local a,o,i=g(e.name,e.type,e.class); if t~='*'then 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)); +local t=d(self.cache,i,'*',a); +if t then n(t,e);end +end +self.cache=self.cache or r({},w); +local a=d(self.cache,i,t,a)or +l(self.cache,i,t,a,r({},j)); if not a[e[o:lower()]]then a[e[o:lower()]]=true; -h(a,e); +n(a,e); end if t=='MX'then self.unsorted[a]=true;end end -local function f(t,e) -return(t.pref==e.pref)and(t.mx#self.server then e.server=1; @@ -1444,28 +1363,28 @@ if e.retries>=#self.server then a[o]=nil; else -t,n=self:getsocket(e.server); +t,i=self:getsocket(e.server); if t then t:send(e.packet);end end end end -if s(a)==nil then -self.active[i]=nil; -end -end -if h==self.best_server then +if h(a)==nil then +self.active[s]=nil; +end +end +if n==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; +return t,i; end function e:settimeout(e) self.timeout=e; end function e:receive(t) -self.time=i.gettime(); +self.time=s.gettime(); t=t or self.socket; local e; for a,t in o(t)do @@ -1476,31 +1395,29 @@ if e and self.active[e.header.id] and self.active[e.header.id][e.question.raw]then for a,t in o(e.answer)do -if t.name:sub(-#e.question[1].name,-1)==e.question[1].name then self:remember(t,e.question[1].type) end -end local t=self.active[e.header.id]; t[e.question.raw]=nil; -if not s(t)then self.active[e.header.id]=nil;end -if not s(self.active)then self:closeall();end +if not h(t)then self.active[e.header.id]=nil;end +if not h(self.active)then self:closeall();end local e=e.question[1]; -local t=r(self.wanted,e.class,e.type,e.name); +local t=d(self.wanted,e.class,e.type,e.name); if t then 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 -end -end -return e; -end -function e:feed(a,e,t) -self.time=i.gettime(); -local e=self:decode(e,t); +if u.status(e)=="suspended"then u.resume(e);end +end +l(self.wanted,e.class,e.type,e.name,nil); +end +end +end +end +end +return e; +end +function e:feed(a,t,e) +self.time=s.gettime(); +local e=self:decode(t,e); if e and self.active[e.header.id] and self.active[e.header.id][e.question.raw]then for a,t in o(e.answer)do @@ -1508,34 +1425,34 @@ end local t=self.active[e.header.id]; t[e.question.raw]=nil; -if not s(t)then self.active[e.header.id]=nil;end -if not s(self.active)then self:closeall();end +if not h(t)then self.active[e.header.id]=nil;end +if not h(self.active)then self:closeall();end local e=e.question[1]; if e then -local t=r(self.wanted,e.class,e.type,e.name); +local t=d(self.wanted,e.class,e.type,e.name); if t then 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); +if u.status(e)=="suspended"then u.resume(e);end +end +l(self.wanted,e.class,e.type,e.name,nil); end end end return e; end function e:cancel(t,a,i) -local e=r(self.wanted,t,a,i); +local e=d(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); +if u.status(e)=="suspended"then u.resume(e);end +end +l(self.wanted,t,a,i,nil); end end function e:pulse() while self:receive()do end -if not s(self.active)then return nil;end -self.time=i.gettime(); +if not h(self.active)then return nil;end +self.time=s.gettime(); for a,t in o(self.active)do for o,e in o(t)do if self.time>=e.retry then @@ -1546,8 +1463,8 @@ end if e.delay>#self.delays then t[o]=nil; -if not s(t)then self.active[a]=nil;end -if not s(self.active)then return nil;end +if not h(t)then self.active[a]=nil;end +if not h(self.active)then return nil;end else local t=self.socket[e.server]; if t then t:send(e.packet);end @@ -1556,22 +1473,22 @@ end end end -if s(self.active)then return true;end +if h(self.active)then return true;end return nil; end -function e:lookup(o,a,t) -self:query(o,a,t) +function e:lookup(a,e,t) +self:query(a,e,t) while self:pulse()do local e={} -for t,a in c(self.socket)do +for t,a in m(self.socket)do e[t]=a end -i.select(e,nil,4) -end -return self:peek(o,a,t); -end -function e:lookupex(o,t,e,a) -return self:peek(t,e,a)or self:query(t,e,a); +s.select(e,nil,4) +end +return self:peek(a,e,t); +end +function e:lookupex(o,a,t,e) +return self:peek(a,t,e)or self:query(a,t,e); end function e:tohostname(e) return t.lookup(e:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)","%4.%3.%2.%1.in-addr.arpa."),"PTR"); @@ -1588,31 +1505,31 @@ type=t.type, class=t.class }; -local function n(t,e) +local function s(t,e) return(i[e]and i[e][t[e]])or''; end -function e.print(e) -for o,t in o{'id','qr','opcode','aa','tc','rd','ra','z', +function e.print(t) +for o,e in o{'id','qr','opcode','aa','tc','rd','ra','z', 'rcode','qdcount','ancount','nscount','arcount'}do -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 t; -for s,i in o({'answer','authority','additional'})do -for s,e in o(e[i])do -for h,o in o({'name','type','class','ttl','rdlength'})do -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))); +f(a.format('%-30s','header.'..e),t.header[e],s(t.header,e)); +end +for e,t in m(t.question)do +f(a.format('question[%i].name ',e),t.name); +f(a.format('question[%i].type ',e),t.type); +f(a.format('question[%i].class ',e),t.class); +end +local r={name=1,type=1,class=1,ttl=1,rdlength=1,rdata=1}; +local e; +for n,i in o({'answer','authority','additional'})do +for h,n in o(t[i])do +for o,t in o({'name','type','class','ttl','rdlength'})do +e=a.format('%s[%i].%s',i,h,t); +f(a.format('%-30s',e),n[t],s(n,t)); +end +for t,o in o(n)do +if not r[t]then +e=a.format('%s[%i].%s',i,h,t); +f(a.format('%-30s %s',p(e),p(o))); end end end @@ -1620,9 +1537,9 @@ end function t.resolver() local t={active={},cache={},unsorted={},wanted={},best_server=1}; -u(t,e); -u(t.cache,y); -u(t.unsorted,{__mode='kv'}); +r(t,e); +r(t.cache,w); +r(t.unsorted,{__mode='kv'}); return t; end local e=t.resolver(); @@ -1660,192 +1577,208 @@ return t; end) package.preload['net.adns']=(function(...) +local _ENV=_ENV; +local function o(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local c=require"net.server"; local a=require"net.dns"; -local e=require"util.logger".init("adns"); -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); +local t=require"util.logger".init("adns"); +local e,e=table.insert,table.remove; +local n,h,l=coroutine,tostring,pcall; +local function u(a,a,t,e)return(e-t)+1;end +o"adns" +function lookup(d,e,s,r) +return n.wrap(function(o) +if o then +t("debug","Records for %s already cached, using those...",e); +d(o); return; end -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 -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,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",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) +t("debug","Records for %s not in cache, sending query (%s)...",e,h(n.running())); +local i,o=a.query(e,s,r); +if i then +n.yield({r or"IN",s or"A",e,n.running()}); +t("debug","Reply for %s (%s)",e,h(n.running())); +end +if i then +i,o=l(d,a.peek(e,s,r)); +else +t("error","Error sending DNS query: %s",o); +i,o=l(d,nil,o); +end +if not i then +t("error","Error in DNS response handler: %s",h(o)); +end +end)(a.peek(e,s,r)); +end +function cancel(e,o,i) +t("warn","Cancelling DNS lookup for %s",h(e[3])); +a.cancel(e[1],e[2],e[3],e[4],o); +end +function new_async_socket(i,o) local n=""; local s={}; -local t={}; +local e={}; local h; -function s.onincoming(o,e) -if e then -a.feed(t,e); -end -end -function s.ondisconnect(o,a) -if a then -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]); -end -i:servfail(o); -end -end -t,h=c.wrapclient(o,"dns",53,s); -if not t then +function s.onincoming(o,t) +if t then +a.feed(e,t); +end +end +function s.ondisconnect(a,i) +if i then +t("warn","DNS socket for %s disconnected: %s",n,i); +local e=o.server; +if o.socketset[a]==o.best_server and o.best_server==#e then +t("error","Exhausted all %d configured DNS servers, next lookup will try %s again",#e,e[1]); +end +o:servfail(a); +end +end +e,h=c.wrapclient(i,"dns",53,s); +if not e then return nil,h; end -t.settimeout=function()end -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 -a.socket_wrapper_set(r); -return{ -lookup=u; -cancel=d; -new_async_socket=r; -}; +e.settimeout=function()end +e.setsockname=function(t,...)return i:setsockname(...);end +e.setpeername=function(o,...)n=(...);local a,t=i:setpeername(...);o:set_send(u);return a,t;end +e.connect=function(t,...)return i:connect(...)end +e.send=function(a,e) +t("debug","Sending DNS query to %s",n); +return i:send(e); +end +return e; +end +a.socket_wrapper_set(new_async_socket); +return _M; end) package.preload['net.server']=(function(...) -local c=function(e) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local m=function(e) return _G[e] end -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 W,e=require("util.logger").init("socket"),table.concat; +local n=function(...)return W("debug",e{...});end +local E=function(...)return W("warn",e{...});end +local ce=1 +local f=m"type" +local b=m"pairs" +local ue=m"ipairs" +local y=m"tonumber" +local l=m"tostring" +local t=m"table" +local a=m"string" +local e=m"coroutine" +local Y=math.min +local re=math.huge +local pe=t.concat +local he=t.insert +local we=a.sub +local fe=e.wrap +local me=e.yield +local O,e=pcall(require,"ssl") +local g=m"socket"or require"socket" +local F=g.gettime +local ee=g.dns.getaddrinfo +local ye=(O and e.wrap) +local te=g.bind +local ve=g.select +local I +local B +local ae +local G +local J +local u +local ie +local X +local se +local ne +local oe +local Q +local s +local le +local Z +local de +local p +local i local P -local le -local B -local K -local m -local re -local oe -local ae -local te -local ie -local G -local d -local se -local Q -local ne +local r +local h +local T local v -local h -local F -local l -local s -local _ -local b -local y +local w local k local x local a local o -local g -local W +local q +local C local M -local I -local O +local A local j -local A -local J -local n -local E -local T local N +local K +local d local H local S -local C -local q +local R +local D +local L local z -local R -v={} +local _ +local U +p={} +i={} +r={} +P={} h={} -l={} -F={} -s={} -b={} -y={} -_={} +v={} +w={} +T={} k={} a=0 o=0 -g=0 -W=0 +q=0 +C=0 M=0 -I=1 -O=0 +A=1 j=128 -A=10 -E=51e3*1024 -T=25e3*1024 -N=30 -H=6e4 -S=6*60*60 +N=10 +H=51e3*1024 +S=25e3*1024 +R=30 +D=6e4 +L=14*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()) +_=(e and math.huge)or g._SETSIZE or 1024 +z=g._SETSIZE or 1024 +U=30 +ne=function(y,t,m,c,g,w) +if t:getfd()>=_ then +E("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 v,e=y.onconnect,y.ondisconnect local b=t.accept local e={} e.shutdown=function()end @@ -1863,110 +1796,109 @@ end e.close=function() t:close() -o=d(l,t,o) -a=d(h,t,a) -v[c..":"..u]=nil; -s[t]=nil +o=s(r,t,o) +a=s(i,t,a) +p[m..":"..c]=nil; +h[t]=nil e=nil t=nil -i"server.lua: closed server handler and removed sockets from list" +n"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) +a=s(i,t,a) if o then -s[t]=nil +h[t]=nil t:close() t=nil; end e.paused=true; -i("server.lua: server [",c,"]:",u," paused") +n("server.lua: server [",m,"]:",c," paused") end end e.resume=function() if e.paused then if not t then -t=de(c,u,j); +t=te(m,c,j); t:settimeout(0) end -a=m(h,t,a) -s[t]=e +a=u(i,t,a) +h[t]=e k[e]=nil e.paused=false; -i("server.lua: server [",c,"]:",u," resumed") +n("server.lua: server [",m,"]:",c," resumed") end end e.ip=function() -return c +return m end e.serverport=function() -return u +return c end e.socket=function() return t end e.readbuffer=function() -if a>=q or o>=q then +if a>=z or o>=z then e.pause() -k[e]=n -i("server.lua: refused new client connection: server full") +k[e]=d +n("server.lua: refused new client connection: server full") return false end -local t,s=b(t) +local t,o=b(t) if t then local o,a=t:getpeername() -local e,n,t=Q(e,y,t,o,u,a,g,w) -if t then +local t,i,e=Z(e,y,t,o,c,a,g,w) +if e then return false end 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); +n("server.lua: accepted new client connection from ",l(o),":",l(a)," to ",l(c)) +if v and not w then +return v(t); end return; -elseif s then -i("server.lua: error with new client connection: ",r(s)) +elseif o then +n("server.lua: error with new client connection: ",l(o)) e.pause() -k[e]=n +k[e]=d return false end end return e end -Q=function(v,f,t,O,J,I,N,g) -if t:getfd()>=z then -L("server.lua: Disallowed FD number: "..t:getfd()) +Z=function(p,f,t,z,K,A,N,g) +if t:getfd()>=_ then +E("server.lua: Disallowed FD number: "..t:getfd()) t:close() -if v then -k[v]=n -v.pause() +if p then +k[p]=d +p.pause() end return nil,nil,"fd-too-large" end t:settimeout(0) -local w -local z +local y +local _ local q -local Y +local V local P=f.onincoming -local F=f.onstatus +local Y=f.onstatus local k=f.ondisconnect -local L=f.ondrain +local D=f.ondrain local Q=f.onreadtimeout; -local C=f.ondetach -local p={} -local u=0 -local G -local K -local D +local R=f.ondetach +local b={} local c=0 +local B +local L +local m=0 local j=false -local A=false -local S,H=0,0 -local E=E -local T=T -local e=p +local E=false +local W,F=0,0 +local H=H +local S=S +local e=b e.dispatch=function() return P end @@ -1975,30 +1907,30 @@ end e.onreadtimeout=Q; e.setlistener=function(a,t) -if C then -C(a) +if R then +R(a) end P=t.onincoming k=t.ondisconnect -F=t.onstatus -L=t.ondrain +Y=t.onstatus +D=t.ondrain e.onreadtimeout=t.onreadtimeout -C=t.ondetach +R=t.ondetach end e.getstats=function() -return H,S +return F,W end e.ssl=function() -return Y +return V end e.sslctx=function() return g end -e.send=function(n,o,i,a) -return w(t,o,i,a) +e.send=function(n,o,a,i) +return y(t,o,a,i) end e.receive=function(o,a) -return z(t,o,a) +return _(t,o,a) end e.shutdown=function(a) return q(t,a) @@ -2009,85 +1941,85 @@ end return false,"setoption not implemented"; end -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) +e.force_close=function(t,a) +if c~=0 then +n("server.lua: discarding unwritten data for ",l(z),":",l(A)) +c=0; +end +return t:close(a); +end +e.close=function(l,d) if not e then return true;end -a=d(h,t,a) -b[e]=nil -if u~=0 then +a=s(i,t,a) +v[e]=nil +if c~=0 then e.sendbuffer() -if u~=0 then +if c~=0 then if e then e.write=nil end -G=true +B=true return false end end if t then x=q and q(t) t:close() -o=d(l,t,o) -s[t]=nil +o=s(r,t,o) +h[t]=nil t=nil else -i"server.lua: socket already closed" +n"server.lua: socket already closed" end if e then -y[e]=nil -_[e]=nil +w[e]=nil +T[e]=nil local t=e; e=nil if k then -k(t,n or false); +k(t,d or false); k=nil end end -if v then -v.remove() -end -i"server.lua: closed client handler and removed socket from list" +if p then +p.remove() +end +n"server.lua: closed client handler and removed socket from list" return true end e.server=function() -return v +return p end e.ip=function() -return O +return z end e.serverport=function() -return J +return K end e.clientport=function() -return I +return A end e.port=e.clientport -local v=function(i,a) +local p=function(i,a) if not e then return false end -c=c+#a -if c>E then -_[e]="send buffer exceeded" -e.write=B +m=m+#a +if m>H then +T[e]="send buffer exceeded" +e.write=G return false -elseif t and not l[t]then -o=m(l,t,o) -end -u=u+1 -p[u]=a +elseif t and not r[t]then +o=u(r,t,o) +end +c=c+1 +b[c]=a if e then -y[e]=y[e]or n +w[e]=w[e]or d end return true end -e.write=v +e.write=p e.bufferqueue=function(t) -return p +return b end e.socket=function(a) return t @@ -2097,27 +2029,27 @@ return N end e.set_send=function(a,t) -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) +y=t or y +return y +end +e.bufferlen=function(o,a,t) +H=t or H +S=a or S +return m,S,H +end +e.lock_read=function(n,o) if o==true then local o=a -a=d(h,t,a) -b[e]=nil +a=s(i,t,a) +v[e]=nil if a~=o then j=true end elseif o==false then if j then j=false -a=m(h,t,a) -b[e]=n +a=u(i,t,a) +v[e]=d end end return j @@ -2131,209 +2063,207 @@ e.lock=function(i,a) e.lock_read(a) if a==true then -e.write=B +e.write=G local a=o -o=d(l,t,o) -y[e]=nil +o=s(r,t,o) +w[e]=nil if o~=a then -A=true +E=true end elseif a==false then -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) +e.write=p +if E then +E=false +p("") +end +end +return j,E +end +local p=function() +local a,t,o=_(t,N) if not t or(t=="wantread"or t=="timeout")then -local a=a or o or"" -local o=#a -if o>T then +local o=a or o or"" +local a=#o +if a>S then e:close("receive buffer exceeded") return false end -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 +local a=a*ce +F=F+a +M=M+a +v[e]=d +return P(e,o,t) +else +n("server.lua: client ",l(z),":",l(A)," read error: ",l(t)) x=e and e:force_close(t) return false end end -local y=function() -local f,a,s,h,m; +local v=function() +local f,a,h,i,u; if t then -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; +i=pe(b,"",1,c) +f,a,h=y(t,i,1,m) +u=(f or h or 0)*ce +W=W+u +C=C+u +for e=c,1,-1 do +b[e]=nil +end +else +f,a,u=false,"unexpected close",0; end if f then -u=0 c=0 -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() +m=0 +o=s(r,t,o) +w[e]=nil +if D then +D(e) +end +x=L and e:starttls(nil) +x=B and e:force_close() return true -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 +elseif h and(a=="timeout"or a=="wantwrite")then +i=we(i,h+1,m) +b[1]=i +c=1 +m=m-h +w[e]=d return true else -i("server.lua: client ",r(O),":",r(I)," write error: ",r(a)) -K=true +n("server.lua: client ",l(z),":",l(A)," write error: ",l(a)) x=e and e:force_close(a) return false end end -local c; +local d; function e.set_sslctx(w,t) g=t; -local s,r -c=me(function(n) +local m,l +d=fe(function(h) local t -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() +for d=1,U do +o=(l and s(r,h,o))or o +a=(m and s(i,h,a))or a +m,l=nil,nil +d,t=h:dohandshake() if not t then -i("server.lua: ssl handshake done") -e.readbuffer=v -e.sendbuffer=y -c=F and F(e,"ssl-handshake-complete") +n("server.lua: ssl handshake done") +e.readbuffer=p +e.sendbuffer=v +d=Y and Y(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) +if c~=0 then +o=u(r,h,o) +end +end +a=u(i,h,a) return true else if t=="wantwrite"then -o=m(l,n,o) -r=true +o=u(r,h,o) +l=true elseif t=="wantread"then -a=m(h,n,a) -s=true +a=u(i,h,a) +m=true else break; end t=nil; -ye() +me() end end t="ssl handshake error: "..(t or"handshake too long"); -i("server.lua: ",t); +n("server.lua: ",t); x=e and e:force_close(t) return false,t end ) end -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" -D=true +if O then +e.starttls=function(f,m) +if m then +e:set_sslctx(m); +end +if c>0 then +n"server.lua: we need to do tls, but delaying until send buffer empty" +L=true return end -i("server.lua: attempting to start tls on "..r(t)) -local n,u=t -t,u=be(t,g) +n("server.lua: attempting to start tls on "..l(t)) +local c,m=t +t,m=ye(t,g) if not t then -i("server.lua: error while starting tls on client: ",r(u or"unknown error")) -return nil,u +n("server.lua: error while starting tls on client: ",l(m or"unknown error")) +return nil,m end t:settimeout(0) -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 +y=t.send +_=t.receive +q=I +h[t]=e +a=u(i,t,a) +a=s(i,c,a) +o=s(r,c,o) +h[c]=nil e.starttls=nil -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..." +L=nil +V=true +e.readbuffer=d +e.sendbuffer=d +return d(t) +end +end +e.readbuffer=p +e.sendbuffer=v +y=t.send +_=t.receive +q=(V and I)or t.shutdown +h[t]=e +a=u(i,t,a) +if g and O then +n"server.lua: auto-starting ssl negotiation..." e.autostart_ssl=true; -local e,t=e:starttls(g); -if e==false then -return nil,nil,t +local t,e=e:starttls(g); +if t==false then +return nil,nil,e end end return e,t end -V=function() -end -B=function() +I=function() +end +G=function() return false end -m=function(a,t,e) -if not a[t]then +u=function(t,a,e) +if not t[a]then e=e+1 -a[e]=t -a[t]=e -end -return e; -end -d=function(e,a,t) -local i=e[a] -if i then -e[a]=nil +t[e]=a +t[a]=e +end +return e; +end +s=function(e,i,t) +local a=e[i] +if a then +e[i]=nil local o=e[t] e[t]=nil -if o~=a then -e[o]=i -e[i]=o +if o~=i then +e[o]=a +e[a]=o end return t-1 end return t end -G=function(e) -o=d(l,e,o) -a=d(h,e,a) -s[e]=nil +Q=function(e) +o=s(r,e,o) +a=s(i,e,a) +h[e]=nil e:close() end local function x(e,t,o) @@ -2356,342 +2286,425 @@ end e:set_mode("*a"); end -re=function(e,t,d,l,r) -e=e or"*" +ie=function(t,e,d,l,r) +t=t or"*" local o -if w(d)~="table"then +if f(d)~="table"then o="invalid listener table" -elseif w(e)~="string"then +elseif f(t)~="string"then o="invalid address" -elseif w(t)~="number"or not(t>=0 and t<=65535)then +elseif f(e)~="number"or not(e>=0 and e<=65535)then o="invalid port" -elseif v[e..":"..t]then -o="listeners on '["..e.."]:"..t.."' already exist" -elseif r and not U then +elseif p[t..":"..e]then +o="listeners on '["..t.."]:"..e.."' already exist" +elseif r and not O then o="luasec not found" end if o then -L("server.lua, [",e,"]:",t,": ",o) +E("server.lua, [",t,"]:",e,": ",o) return nil,o end -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 +local o,s=te(t,e,j) +if s then +E("server.lua, [",t,"]:",e,": ",s) +return nil,s +end +local s,d=ne(d,o,t,e,l,r) +if not s then o:close() return nil,d end o:settimeout(0) -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]; +a=u(i,o,a) +p[t..":"..e]=s +h[o]=s +n("server.lua: new "..(r and"ssl "or"").."server listener on '[",t,"]:",e,"'") +return s end se=function(t,e) -local a=v[t..":"..e] +return p[t..":"..e]; +end +le=function(t,e) +local a=p[t..":"..e] if not a then -return nil,"no server found on '["..t.."]:"..r(e).."'" +return nil,"no server found on '["..t.."]:"..l(e).."'" end a:close() -v[t..":"..e]=nil +p[t..":"..e]=nil return true end -K=function() -for t,e in D(s)do -e:close() -s[t]=nil +J=function() +for e,t in b(h)do +t:close() +h[e]=nil end a=0 o=0 -g=0 -v={} +q=0 +p={} +i={} +r={} +P={} h={} -l={} -F={} -s={} -end -ie=function() +end +oe=function() return{ -select_timeout=I; -select_sleep_time=O; +select_timeout=A; 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; +max_send_buffer_size=H; +max_receive_buffer_size=S; +select_idle_check_interval=R; +send_timeout=D; +read_timeout=L; +max_connections=z; +max_ssl_handshake_roundtrips=U; +highest_allowed_fd=_; +accept_retry_interval=N; } end -ne=function(e) -if w(e)~="table"then +de=function(e) +if f(e)~="table"then return nil,"invalid settings table" end -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 +A=y(e.select_timeout)or A +H=y(e.max_send_buffer_size)or H +S=y(e.max_receive_buffer_size)or S +R=y(e.select_idle_check_interval)or R +j=y(e.tcp_backlog)or j +D=y(e.send_timeout)or D +L=y(e.read_timeout)or L +N=y(e.accept_retry_interval)or N +z=e.max_connections or z +U=e.max_ssl_handshake_roundtrips or U +_=e.highest_allowed_fd or _ +return true +end +X=function(e) +if f(e)~="function"then +return nil,"invalid listener function" +end +q=q+1 +P[q]=e return true end -oe=function(e) -if w(e)~="function"then -return nil,"invalid listener function" -end -g=g+1 -F[g]=e -return true -end -le=function() -return M,W,a,o,g -end -local t; -local function f(e) -t=not not e; -end -P=function(a) -if t then return"quitting";end -if a then t="once";end -local e=ue; +local l do +local a={}; +local e={}; +function l(t,a) +local o=F(); +t=t+o; +if t>=o then +he(e,{t,a}); +else +local e=a(o); +if e and f(e)=="number"then +return l(e,a); +end +end +end +X(function(t) +if#e>0 then +for o,t in b(e)do +he(a,t); +end +e={}; +end +local e=re; +for n,o in b(a)do +local i,o=o[1],o[2]; +if i<=t then +a[n]=nil; +local t=o(t); +if f(t)=="number"then +l(t,o); +e=Y(e,t); +end +else +e=Y(e,i-t); +end +end +return e; +end); +end +ae=function() +return M,C,a,o,q +end +local e; +local function p(t) +e=t; +end +B=function(t) +if e then return"quitting";end +if t then e="once";end +d=F() repeat -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 ee(o)do -local e=s[t] -if e then -e.readbuffer() -else -G(t) -i"server.lua: found no handler and closed socket (readlist)" -end -end -for e,t in D(_)do +local t=re; +for e=1,q do +local e=P[e](d) +if e then t=Y(t,e);end +end +local t,a,o=ve(i,r,Y(A,t)) +for t,e in ue(t)do +local t=h[e] +if t then +t.readbuffer() +else +Q(e) +n"server.lua: found no handler and closed socket (readlist)" +end +end +for t,e in ue(a)do +local t=h[e] +if t then +t.sendbuffer() +else +Q(e) +n"server.lua: found no handler and closed socket (writelist)" +end +end +for e,t in b(T)do e.disconnect()(e,t) 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 +T[e]=nil; +end +d=F() +if d-K>R then +K=d +for e,t in b(w)do +if d-t>D then e.disconnect()(e,"send timeout") e:force_close() end end -for e,t in D(b)do -if n-t>S then +for e,t in b(v)do +if d-t>L 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=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 +v[e]=d +end +end +end +end +for e,t in b(k)do +if d-t>N then k[e]=nil; e.resume(); end end -ve(O) -until t; -if a and t=="once"then t=nil;return;end -K(); +until e; +if e=="once"then e=nil;return;end +J(); return"quitting" end -local function u() -return P(true); -end -local function d() +local function k() +return B(true); +end +local function y() return"select"; end -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=m(l,a,o) -if t.onconnect then -local a=e.sendbuffer; +local c=function(l,e,t,n,d,s) +local e,t,d=Z(nil,n,l,e,t,"clientport",d,s) +if not e then return nil,d end +h[t]=e +if not s then +a=u(i,t,a) +o=u(r,t,o) +if n.onconnect then +local t=e.sendbuffer; e.sendbuffer=function() -e.sendbuffer=a; -t.onconnect(e); -return a(); -end -end -end -return e,a -end -local o=function(o,t,s,r,n,a) +e.sendbuffer=t; +n.onconnect(e); +return t(); +end +end +end +return e,t +end +local b=function(a,t,n,s,i,o) local e -if w(s)~="table"then +if f(n)~="table"then e="invalid listener table" -elseif w(o)~="string"then +elseif f(a)~="string"then e="invalid address" -elseif w(t)~="number"or not(t>=0 and t<=65535)then +elseif f(t)~="number"or not(t>=0 and t<=65535)then e="invalid port" -elseif n and not U then +elseif i and not O then e="luasec not found" end -if not a then -local e,t=pe(o) +if ee and not o then +local e,t=ee(a) 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 +o="tcp6" +end +end +local o=g[o or"tcp"] +if f(o)~="function"then e="invalid socket type" end if e then -L("server.lua, addclient: ",e) +E("server.lua, addclient: ",e) return nil,e end -local e,a=a() -if a then -return nil,a +local e,o=o() +if o then +return nil,o end e:settimeout(0) -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 h,o=e:connect(a,t) +if h or o=="timeout"or o=="Operation already in progress"then +return c(e,a,t,n,s,i) +else +return nil,o +end +end +local g=function(e) +local e=e.conn; +o=s(r,e,o) +a=s(i,e,a) +h[e]=nil +end; +local t=function(n,t,d) +local e=n.conn +h[e]=n +if t~=nil then +if t then +a=u(i,e,a) +else +o=s(r,e,o) +end +end +if d~=nil then +if d then +o=u(r,e,o) +else +a=s(i,e,a) +end +end +end +local t=function(e,o,a) +local i=e +if f(e)=="number"then +i={getfd=function()return e;end} +end +local e={ +conn=i; +readbuffer=o or I; +sendbuffer=a or I; +close=g; +setflags=t; +}; +t(e,o,a) +return e +end +m"setmetatable"(h,{__mode="k"}) +m"setmetatable"(v,{__mode="k"}) +m"setmetatable"(w,{__mode="k"}) +K=F() local function a(e) -local t=Y; +local t=W; if e then -Y=e; +W=e; end return t; end return{ -_addtimer=oe, -addclient=o, -wrapclient=i, -loop=P, +_addtimer=X, +add_task=l; +addclient=b, +wrapclient=c, +watchfd=t, +loop=B, link=x, -step=u, -stats=le, -closeall=K, -addserver=re, -getserver=ae, +step=k, +stats=ae, +closeall=J, +addserver=ie, +getserver=se, setlogger=a, -getsettings=ie, -setquitting=f, -removeserver=se, -get_backend=d, -changesettings=ne, +getsettings=oe, +setquitting=p, +removeserver=le, +get_backend=y, +changesettings=de, } end) package.preload['util.xmppstream']=(function(...) +local _ENV=_ENV; +local function o(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local e=require"lxp"; -local b=require"util.stanza"; -local k=b.stanza_mt; -local m=error; +local t=require"util.stanza"; +local p=t.stanza_mt; +local f=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 l=table.insert; +local w=table.concat; +local T=table.remove; +local y=setmetatable; +local x=pcall(e.new,{StartDoctypeDecl=false}); +local z=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 _={ +local _=1024*1024*10; +o"xmppstream" +local k=e.new; +local E={ ["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 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"; +local o="http://etherx.jabber.org/streams"; +local d="\1"; +local b="^([^"..d.."]*)"..d.."?(.*)$"; +_M.ns_separator=d; +_M.ns_pattern=b; +local function s()end +function new_sax_handlers(n,e,h) +local i={}; +local g=e.streamopened; +local v=e.streamclosed; +local u=e.error or function(o,a,e)f("XML stream error: "..t(a)..(e and": "..t(e)or""),2);end; +local k=e.handlestanza; +h=h or s; +local t=e.stream_ns or o; +local m=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={}; +m=t..d..m; +end +local q=t..d..(e.error_tag or"error"); +local j=e.default_ns; +local d={}; local s,e={}; local t=0; -local h=0; -function o:StartElement(m,o) +local r=0; +function i:StartElement(c,o) if e and#s>0 then -d(e,p(s)); +l(e,w(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; +local s,i=c:match(b); +if i==""then +s,i="",s; +end +if s~=j or r>0 then +o.xmlns=s; +r=r+1; end for t=1,#o do local e=o[t]; o[t]=nil; -local t=_[e]; +local t=E[e]; if t then o[t]=o[e]; o[e]=nil; @@ -2701,481 +2714,408 @@ if a then t=self:getcurrentbytecount(); end -if i.notopen then -if m==c then -h=0; -if w then +if n.notopen then +if c==m then +r=0; +if g then if a then -n(t); +h(t); t=0; end -w(i,o); -end -else -l(i,"no-stream",m); +g(n,o); +end +else +u(n,"no-stream",c); end return; end -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); +if s=="jabber:client"and i~="iq"and i~="presence"and i~="message"then +u(n,"invalid-top-level-element"); +end +e=y({name=i,attr=o,tags={}},p); else if a then t=t+self:getcurrentbytecount(); end -d(u,e); +l(d,e); local t=e; -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) +e=y({name=i,attr=o,tags={}},p); +l(t,e); +l(t.tags,e); +end +end +if z then +function i:XmlDecl(e,e,e) if a then -n(self:getcurrentbytecount()); -end -end -end -function o:StartCdataSection() +h(self:getcurrentbytecount()); +end +end +end +function i:StartCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else -n(self:getcurrentbytecount()); -end -end -end -function o:EndCdataSection() +h(self:getcurrentbytecount()); +end +end +end +function i:EndCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else -n(self:getcurrentbytecount()); -end -end -end -function o:CharacterData(o) +h(self:getcurrentbytecount()); +end +end +end +function i:CharacterData(o) if e then if a then t=t+self:getcurrentbytecount(); end -d(s,o); +l(s,o); elseif a then -n(self:getcurrentbytecount()); -end -end -function o:EndElement(o) +h(self:getcurrentbytecount()); +end +end +function i:EndElement(o) if a then t=t+self:getcurrentbytecount() end -if h>0 then -h=h-1; +if r>0 then +r=r-1; end if e then if#s>0 then -d(e,p(s)); +l(e,w(s)); s={}; end -if#u==0 then +if#d==0 then if a then -n(t); +h(t); end t=0; -if o~=b then -y(i,e); -else -l(i,"stream-error",e); +if o~=q then +k(n,e); +else +u(n,"stream-error",e); end e=nil; else -e=x(u); -end -else -if f then -f(i); +e=T(d); +end +else +if v then +v(n); end end end local function a(e) -l(i,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1."); +u(n,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1."); if not e.stop or not e:stop()then -m("Failed to abort parsing"); -end -end -if q then -o.StartDoctypeDecl=a; -end -o.Comment=a; -o.ProcessingInstruction=a; +f("Failed to abort parsing"); +end +end +if x then +i.StartDoctypeDecl=a; +end +i.Comment=a; +i.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; +d={}; +end +local function e(t,e) +n=e; +end +return i,{reset=a,set_session=e}; +end +function new(n,i,o) +local e=0; +local t; if a then -function e(e) -t=t-e; -end -o=o or z; +function t(t) +e=e-t; +end +o=o or _; 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 +f("Stanza size limits are not supported on this version of LuaExpat") +end +local i,n=new_sax_handlers(n,i,t); +local t=k(i,d,false); +local s=t.parse; return{ reset=function() -i=y(n,r,false); -h=i.parse; -t=0; -s.reset(); +t=k(i,d,false); +s=t.parse; +e=0; +n.reset(); end, -feed=function(n,e) +feed=function(n,i) if a then -t=t+#e; -end -local i,e=h(i,e); -if a and t>o then +e=e+#i; +end +local i,t=s(t,i); +if a and e>o then return nil,"stanza-too-large"; end -return i,e; +return i,t; end, -set_session=s.set_session; +set_session=n.set_session; }; end -return{ -ns_separator=r; -ns_pattern=g; -new_sax_handlers=w; -new=u; -}; +return _M; end) package.preload['util.jid']=(function(...) -local o=select; -local a,i=string.match,string.sub; +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local t,s=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 h={ +local d=require"util.encodings".stringprep.nameprep; +local r=require"util.encodings".stringprep.resourceprep; +local n={ [" "]="\\20";['"']="\\22"; ["&"]="\\26";["'"]="\\27"; ["/"]="\\2f";[":"]="\\3a"; ["<"]="\\3c";[">"]="\\3e"; ["@"]="\\40";["\\"]="\\5c"; }; -local s={}; -for e,t in pairs(h)do s[t]=e;end -local e=nil; -local function t(e) +local i={}; +for e,t in pairs(n)do i[t]=e;end +a"jid" +local function o(e) if not e then return;end -local i,t=a(e,"^([^@/]+)@()"); -local t,o=a(e,"^([^@/]+)()",t) -if i and not t then return nil,nil,nil;end -local a=a(e,"^/(.+)$",o); -if(not t)or((not a)and#e>=o)then return nil,nil,nil;end -return i,t,a; -end -local function m(e) -local t,e=t(e); +local i,a=t(e,"^([^@/]+)@()"); +local a,o=t(e,"^([^@/]+)()",a) +if i and not a then return nil,nil,nil;end +local t=t(e,"^/(.+)$",o); +if(not a)or((not t)and#e>=o)then return nil,nil,nil;end +return i,a,t; +end +split=o; +function bare(e) +local t,e=o(e); if t and e then return t.."@"..e; end return e; end -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); +local function h(e) +local a,e,t=o(e); +if e then +if s(e,-1,-1)=="."then +e=s(e,1,-2); +end +e=d(e); if not e then return;end -if t then -t=l(t); -if not t then return;end -end if a then -a=d(a); +a=l(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 +if t then +t=r(t); +if not t then return;end +end +return a,e,t; +end +end +prepped_split=h; +function prep(e) +local a,e,t=h(e); +if e then +if a then +e=a.."@"..e; +end +if t then +e=e.."/"..t; +end +end +return e; +end +function join(a,e,t) +if a and e and t then return a.."@"..e.."/"..t; -elseif a then +elseif a and e then return a.."@"..e; -elseif t then +elseif e and t then return e.."/"..t; -end -return e; -end -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 +elseif e then +return e; +end +return nil; +end +function compare(t,e) +local n,i,s=o(t); +local a,t,e=o(e); +if((a~=nil and a==n)or a==nil)and +((t~=nil and t==i)or t==nil)and +((e~=nil and e==s)or e==nil)then return true end return false end -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; -}; +function escape(e)return e and(e:gsub(".",n));end +function unescape(e)return e and(e:gsub("\\%x%x",i));end +return _M; end) package.preload['util.events']=(function(...) -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 _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local i=pairs; +local r=table.insert; +local s=table.sort; +local h=setmetatable; +local n=next; +a"events" +function new() local 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; +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 +r(t,e); +end +s(t,function(a,t)return e[a]>e[t];end); +o[a]=t; return t; end; -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; +h(t,{__index=o}); +local function h(o,i,n) +local a=e[o]; +if a then +a[i]=n or 0; +else +a={[i]=n or 0}; +e[o]=a; +end +t[o]=nil; end; -local function r(e,a) -local t=i[e]; -if t then -t[a]=nil; -o[e]=nil; -if h(t)==nil then -i[e]=nil; +local function s(o,i) +local a=e[o]; +if a then +a[i]=nil; +t[o]=nil; +if n(a)==nil then +e[o]=nil; end end end; -local function m(e) -return o[e]; -end; -local function f(e) -for e,t in a(e)do -s(e,t); +local function n(e) +for t,e in i(e)do +h(t,e); end end; -local function c(e) -for t,e in a(e)do -r(t,e); +local function a(e) +for t,e in i(e)do +s(t,e); end end; -local function h(e,t) -local e=o[e]; +local function o(e,...) +local e=t[e]; if e then -for a=1,#e do -local e=e[a](t); +for t=1,#e do +local e=e[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=s; -remove_handler=r; -add_handlers=f; -remove_handlers=c; -get_handlers=m; -wrappers={ -add_handler=d; -remove_handler=h; +add_handler=h; +remove_handler=s; +add_handlers=n; +remove_handlers=a; +fire_event=o; +_handlers=t; +_event_map=e; }; -add_wrapper=d; -remove_wrapper=h; -fire_event=u; -_handlers=o; -_event_map=i; -}; -end -return{ -new=u; -}; +end +return _M; end) package.preload['util.dataforms']=(function(...) -local e=setmetatable; -local t,i=pairs,ipairs; -local d,r,u=tostring,type,next; -local h=table.concat; -local m=require"util.stanza"; -local l=require"util.jid".prep; +local _ENV=_ENV; +local function o(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local a=setmetatable; +local e,i=pairs,ipairs; +local h,r,c=tostring,type,next; +local s=table.concat; +local u=require"util.stanza"; +local d=require"util.jid".prep; +o"dataforms" +local l='jabber:x:data'; local n={}; -local c='jabber:x:data'; -local s={}; -local a={__index=s}; -function n.new(t) -return e(t,a); -end -function n.from_stanza(e) +local e={__index=n}; +function new(t) +return a(t,e); +end +function from_stanza(e) local o={ title=e:get_child_text("title"); instructions=e:get_child_text("instructions"); }; -for e in e:childtags("field")do +for t in e:childtags("field")do local a={ -name=e.attr.var; -label=e.attr.label; -type=e.attr.type; -required=e:get_child("required")and true or nil; -value=e:get_child_text("value"); +name=t.attr.var; +label=t.attr.label; +type=t.attr.type; +required=t:get_child("required")and true or nil; +value=t:get_child_text("value"); }; o[#o+1]=a; if a.type then -local t={}; +local e={}; if a.type:match"list%-"then -for e in e:childtags("option")do -t[#t+1]={label=e.attr.label,value=e:get_child_text("value")}; -end -for e in e:childtags("value")do -t[#t+1]={label=e.attr.label,value=e:get_text(),default=true}; +for t in t:childtags("option")do +e[#e+1]={label=t.attr.label,value=t:get_child_text("value")}; +end +for t in t:childtags("value")do +e[#e+1]={label=t.attr.label,value=t:get_text(),default=true}; end elseif a.type:match"%-multi"then -for e in e:childtags("value")do -t[#t+1]=e.attr.label and{label=e.attr.label,value=e:get_text()}or e:get_text(); +for t in t:childtags("value")do +e[#e+1]=t.attr.label and{label=t.attr.label,value=t:get_text()}or t:get_text(); end if a.type=="text-multi"then -a.value=h(t,"\n"); -else -a.value=t; +a.value=s(e,"\n"); +else +a.value=e; end end end end return new(o); end -function s.form(t,n,e) -local e=m.stanza("x",{xmlns=c,type=e or"form"}); +function n.form(t,n,e) +local e=u.stanza("x",{xmlns=l,type=e or"form"}); if t.title then e:tag("title"):text(t.title):up(); end @@ -3193,7 +3133,7 @@ :add_child(t) :up(); else -e:tag("value"):text(d(t)):up(); +e:tag("value"):text(h(t)):up(); end elseif a=="boolean"then e:tag("value"):text((t and"1")or"0"):up(); @@ -3220,7 +3160,7 @@ a=true; end else -e:tag("option",{label=t}):tag("value"):text(d(t)):up():up(); +e:tag("option",{label=t}):tag("value"):text(h(t)):up():up(); end end elseif a=="list-multi"then @@ -3231,7 +3171,7 @@ e:tag("value"):text(t.value):up(); end else -e:tag("option",{label=t}):tag("value"):text(d(t)):up():up(); +e:tag("option",{label=t}):tag("value"):text(h(t)):up():up(); end end end @@ -3244,7 +3184,7 @@ return e; end local e={}; -function s.data(t,n) +function n.data(t,n) local o={}; local a={}; for i,t in i(t)do @@ -3266,7 +3206,7 @@ end end end -if u(a)then +if c(a)then return o,a; end return o; @@ -3285,7 +3225,7 @@ e["jid-single"]= function(t,o) local t=t:get_child_text("value") -local a=l(t); +local a=d(t); if a and#a>0 then return a elseif t then @@ -3300,14 +3240,14 @@ local t={}; for e in o:childtags("value")do local e=e:get_text(); -local o=l(e); +local o=d(e); a[#a+1]=o; if e and not o then t[#t+1]=("Invalid JID: "..e); end end if#a>0 then -return a,(#t>0 and h(t,"\n")or nil); +return a,(#t>0 and s(t,"\n")or nil); elseif i then return nil,"Required value missing"; end @@ -3324,7 +3264,7 @@ function(t,a) local t,a=e["list-multi"](t,a); if t then -t=h(t,"\n"); +t=s(t,"\n"); end return t,a; end @@ -3350,24 +3290,35 @@ function(e) return e:get_child_text("value"); end -return n; +return _M; end) package.preload['util.caps']=(function(...) -local d=require"util.encodings".base64.encode; -local l=require"util.hashes".sha1; +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local l=require"util.encodings".base64.encode; +local d=require"util.hashes".sha1; local n,s,h=table.insert,table.sort,table.concat; local r=ipairs; -local e=nil; -local function u(e) -local a,i,o={},{},{}; +a"caps" +function calculate_hash(e) +local a,o,i={},{},{}; 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(i,e.attr.var or""); +n(o,e.attr.var or""); elseif e.name=="x"and e.attr.xmlns=="jabber:x:data"then local t={}; -local i; +local o; for a,e in r(e.tags)do if e.name=="field"and e.attr.var then local a={}; @@ -3377,7 +3328,7 @@ end s(a); if e.attr.var=="FORM_TYPE"then -i=a[1]; +o=a[1]; elseif#a>0 then n(t,e.attr.var.."\0"..h(a,"<")); else @@ -3387,42 +3338,52 @@ end s(t); t=h(t,"<"); -if i then t=i.."\0"..t;end -n(o,t); +if o then t=o.."\0"..t;end +n(i,t); end end s(a); +s(o); 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)); +if#o>0 then o=h(o,"<").."<";else o="";end +if#i>0 then i=h(i,"<"):gsub("%z","<").."<";else i="";end +local e=a..o..i; +local t=l(d(e)); +print(("CAPS %q %q"):format(t,e)); return t,e; end -return{ -calculate_hash=u; -}; +return _M; end) package.preload['util.vcard']=(function(...) -local o=require"util.stanza"; -local a,u=table.insert,table.concat; -local h=type; -local e,s,m=next,pairs,ipairs; -local c,d,l,r; -local f="\n"; -local i; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local i=require"util.stanza"; +local a,h=table.insert,table.concat; +local s=type; +local e,n,c=next,pairs,ipairs; +local d,r,l,u; +local m="\n"; +local o; local function e() error"Not implemented" end local function e() error"Not implemented" end -local function w(e) +local function y(e) return e:gsub("[,:;\\]","\\%1"):gsub("\n","\\n"); end -local function n(e) +local function p(e) return e:gsub("\\?[\\nt:;,]",{ ["\\\\"]="\\", ["\\n"]="\n", @@ -3436,104 +3397,104 @@ [","]="\31", }); end -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(); +local function w(e) +local a=i.stanza(e.name,{xmlns="vcard-temp"}); +local t=o[e.name]; +if t=="text"then +a:text(e[1]); +elseif s(t)=="table"then +if t.types and e.TYPE then +if s(e.TYPE)=="table"then +for o,t in n(t.types)do +for o,e in n(e.TYPE)do +if e:upper()==t then +a:tag(t):up(); break; end end end else -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; +a:tag(e.TYPE:upper()):up(); +end +end +if t.props then +for o,t in n(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; local i=o.behaviour=="repeat-last"and o[#o]; -for o=1,#t do -a:tag(e.values[o]or i):text(t[o]):up(); +for o=1,#e do +a:tag(t.values[o]or i):text(e[o]):up(); end end end return a; end -local function t(e) -local t=o.stanza("vCard",{xmlns="vcard-temp"}); +local function f(t) +local e=i.stanza("vCard",{xmlns="vcard-temp"}); +for a=1,#t do +e:add_child(w(t[a])); +end +return e; +end +function u(e) +if not e[1]or e[1].name then +return f(e) +else +local t=i.stanza("xCard",{xmlns="vcard-temp"}); for a=1,#e do -t:add_child(y(e[a])); +t:add_child(f(e[a])); end return t; end -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) +end +function d(t) t=t :gsub("\r\n","\n") :gsub("\n ","") :gsub("\n\n+","\n"); -local h={}; +local s={}; local e; for t in t:gmatch("[^\n]+")do -local t=n(t); -local s,t,n=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); -n=n:gsub("\29",":"); +local t=p(t); +local n,t,i=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); +i=i:gsub("\29",":"); if#t>0 then -local a={}; -for e,o,i in t:gmatch("\30([^=]+)(=?)([^\30]*)")do -e=e:upper(); -local t={}; -for e in i:gmatch("[^\31]+")do -t[#t+1]=e -t[e]=true; -end -if o=="="then -a[e]=t; -else -a[e]=true; -end -end -t=a; -end -if s=="BEGIN"and n=="VCARD"then +local o={}; +for a,n,i in t:gmatch("\30([^=]+)(=?)([^\30]*)")do +a=a:upper(); +local e={}; +for t in i:gmatch("[^\31]+")do +e[#e+1]=t +e[t]=true; +end +if n=="="then +o[a]=e; +else +o[a]=true; +end +end +t=o; +end +if n=="BEGIN"and i=="VCARD"then e={}; -h[#h+1]=e; -elseif s=="END"and n=="VCARD"then +s[#s+1]=e; +elseif n=="END"and i=="VCARD"then e=nil; -elseif e and i[s]then -local o=i[s]; -local i={name=s}; -e[#e+1]=i; +elseif e and o[n]then +local o=o[n]; +local n={name=n}; +e[#e+1]=n; local s=e; -e=i; +e=n; if o.types then -for o,a in m(o.types)do +for o,a in c(o.types)do local a=a:lower(); if(t.TYPE and t.TYPE[a]==true) or t[a]==true then @@ -3542,12 +3503,12 @@ end end if o.props then -for o,a in m(o.props)do +for o,a in c(o.props)do if t[a]then if t[a]==true then e[a]=true; else -for o,t in m(t[a])do +for o,t in c(t[a])do e[a]=t; end end @@ -3555,9 +3516,9 @@ end end if o=="text"or o.value then -a(e,n); +a(e,i); elseif o.values then -local t="\30"..n; +local t="\30"..i; for t in t:gmatch("\30([^\30]*)")do a(e,t); end @@ -3565,82 +3526,82 @@ e=s; end end -return h; -end -local function n(e) -local t={}; -for a=1,#e do -t[a]=w(e[a]); -end -t=u(t,";"); +return s; +end +local function i(t) +local e={}; +for a=1,#t do +e[a]=y(t[a]); +end +e=h(e,";"); local a=""; -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 o(t) +for t,e in n(t)do +if s(t)=="string"and t~="name"then +a=a..(";%s=%s"):format(t,s(e)=="table"and h(e,",")or e); +end +end +return("%s%s:%s"):format(t.name,a,e) +end +local function t(t) local e={}; a(e,"BEGIN:VCARD") for o=1,#t do -a(e,n(t[o])); +a(e,i(t[o])); end a(e,"END:VCARD") -return u(e,f); -end -function d(e) +return h(e,m); +end +function r(e) if e[1]and e[1].name then -return o(e) -else -local a={}; -for t=1,#e do -a[t]=o(e[t]); -end -return u(a,f); -end -end -local function n(o) -local t=o.name; -local e=i[t]; +return t(e) +else +local o={}; +for a=1,#e do +o[a]=t(e[a]); +end +return h(o,m); +end +end +local function h(i) +local t=i.name; +local e=o[t]; local t={name=t}; if e=="text"then -t[1]=o:get_text(); -elseif h(e)=="table"then +t[1]=i:get_text(); +elseif s(e)=="table"then if e.value then -t[1]=o:get_child_text(e.value)or""; +t[1]=i: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""); +for e=1,#i.tags do +a(t,i.tags[e]:get_text()or""); +end +else +for o=1,#e do +a(t,i:get_child_text(e[o])or""); end end elseif e.names then local e=e.names; for a=1,#e do -if o:get_child(e[a])then +if i:get_child(e[a])then t[1]=e[a]; break; end end end if e.props_verbatim then -for e,a in s(e.props_verbatim)do -t[e]=a; +for a,e in n(e.props_verbatim)do +t[a]=e; 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()); +for o=1,#e do +if i:get_child(e[o])then +a(t.TYPE,e[o]:lower()); end end if#t.TYPE==0 then @@ -3649,9 +3610,9 @@ 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); +for o=1,#e do +local e=e[o] +local o=i:get_child_text(e); if o then t[e]=t[e]or{}; a(t[e],o); @@ -3663,11 +3624,11 @@ end return t; end -local function t(e) +local function i(e) local e=e.tags; local t={}; for o=1,#e do -a(t,n(e[o])); +a(t,h(e[o])); end return t end @@ -3677,16 +3638,16 @@ end if e.name=="xCard"then local a={}; -local e=e.tags; -for o=1,#e do -a[o]=t(e[o]); +local t=e.tags; +for e=1,#t do +a[e]=i(t[e]); end return a elseif e.name=="vCard"then -return t(e) -end -end -i={ +return i(e) +end +end +o={ VERSION="text", FN="text", N={ @@ -3808,159 +3769,188 @@ }, DESC="text", }; -i.LOGO=i.PHOTO; -i.SOUND=i.PHOTO; +o.LOGO=o.PHOTO; +o.SOUND=o.PHOTO; return{ -from_text=c; -to_text=d; +from_text=d; +to_text=r; 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; +to_xep54=u; +lua_to_text=r; +lua_to_xep54=u; +text_to_lua=d; +text_to_xep54=function(...)return u(d(...));end; xep54_to_lua=l; -xep54_to_text=function(...)return d(l(...))end; +xep54_to_text=function(...)return r(l(...))end; }; end) package.preload['util.logger']=(function(...) -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"); +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local e=pcall; +local e=string.find; +local e,o,e=ipairs,pairs,setmetatable; +local a={}; +local e={}; +local t; +function a.init(e) +local a=t(e,"debug"); +local o=t(e,"info"); +local i=t(e,"warn"); +local n=t(e,"error"); return function(t,e,...) if t=="debug"then return a(e,...); elseif t=="info"then -return n(e,...); +return o(e,...); elseif t=="warn"then return i(e,...); elseif t=="error"then -return o(e,...); -end -end -end -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 -local function n() -for t,e in o(t)do +return n(e,...); +end +end +end +function t(o,a) +local t=e[a]; +if not t then +t={}; +e[a]=t; +end +local e=function(e,...) +for i=1,#t do +t[i](o,a,e,...); +end +end +return e; +end +function a.reset() +for t,e in o(e)do for t=1,#e do e[t]=nil; end end end -local function o(e,a) -if not t[e]then -t[e]={a}; -else -t[e][#t[e]+1]=a; -end -end -return{ -init=i; -make_logger=e; -reset=n; -add_level_sink=o; -new=e; -}; +function a.add_level_sink(t,o) +if not e[t]then +e[t]={o}; +else +e[t][#e[t]+1]=o; +end +end +a.new=t; +return a; end) package.preload['util.datetime']=(function(...) +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local e=os.date; local i=os.time; -local w=os.difftime; -local l=tonumber; -local t=nil; -local function c(t) +local u=os.difftime; +local t=error; +local s=tonumber; +a"datetime" +function date(t) return e("!%Y-%m-%d",t); end -local function u(t) +function datetime(t) return e("!%Y-%m-%dT%H:%M:%SZ",t); end -local function m(t) +function time(t) return e("!%H:%M:%S",t); end -local function f(t) +function legacy(t) return e("!%Y%m%dT%H:%M:%S",t); end -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+%-]?.*)$"); +function parse(o) +if o then +local n,h,l,d,r,t,a; +n,h,l,d,r,t,a=o:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d)%.?%d*([Z+%-]?.*)$"); if n then -local u=w(i(e("*t")),i(e("!*t"))); -local t=0; +local u=u(i(e("*t")),i(e("!*t"))); +local o=0; if a~=""and a~="Z"then -local o,a,e=a:match("([+%-])(%d%d):?(%d*)"); -if not o then return;end +local a,t,e=a:match("([+%-])(%d%d):?(%d*)"); +if not a then return;end if#e~=2 then e="0";end -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; -}; +t,e=s(t),s(e); +o=t*60*60+e*60; +if a=="-"then o=-o;end +end +t=(t+u)-o; +return i({year=n,month=h,day=l,hour=d,min=r,sec=t,isdst=false}); +end +end +end +return _M; 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 _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local w=type; +local t,v,p,q=table.insert,table.concat,table.remove,table.sort; +local h=string.char; +local j,l=tostring,tonumber; +local u,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 e=error; +local e,r,b=newproxy,getmetatable,setmetatable; +local c=print; +local a,o=pcall(require,"util.array"); +local f=a and r(o())or{}; local a={}; -local r=w({},{__tostring=function()return"null";end;}); -a.null=r; -local d={ +local n=e and e(true)or{}; +if r and r(n)then +r(n).__tostring=function()return"null";end; +end +a.null=n; +local y={ ["\""]="\\\"",["\\"]="\\\\",["\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 +for e=0,31 do +local t=h(e); +if not y[t]then y[t]=("\\u%.4X"):format(e);end end local function x(e) -if e<128 then return n(e);end +if e<128 then return h(e);end local t=e%64; if e<2048 then local e=(e-t)/64; -return n(128+64+e,128+t); +return h(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); +return h(128+64+32+e,128+o,128+t); end local g={ number=true, @@ -3972,96 +3962,94 @@ __array=true; __hash=true; }; -local o,k,h,u; -function u(a,e) -t(e,"\""..(a:gsub(".",d)).."\""); -end -function h(a,e) +local o,k,d,m; +function m(a,e) +t(e,"\""..(a:gsub(".",y)).."\""); +end +function d(a,e) t(e,"["); if i(a)then for i,a in s(a)do o(a,e); t(e,","); end -y(e); +p(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 +local r={}; +local h={}; +for t,e in s(l)do +a[t]=e; +end +for e,t in u(l)do +local o,i=w(e),w(t); +if g[i]or t==n 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 +h[e]=t; +elseif(g[o]or e==n)and a[e]==nil then +r[e]=t; +end +end +end +if i(r)~=nil or i(h)~=nil or i(a)==nil then t(e,"{"); -local r=#e; +local n=#e; if e.ordered then local a={}; -for e in c(n)do +for e in u(h)do t(a,e); end -j(a); +q(a); for i,a in s(a)do -u(a,e); +m(a,e); t(e,":"); -o(n[a],e); +o(h[a],e); t(e,","); end else -for a,i in c(n)do -u(a,e); +for a,i in u(h)do +m(a,e); t(e,":"); o(i,e); t(e,","); end end -if i(d)~=nil then +if i(r)~=nil then t(e,"\"__hash\":["); -for i,a in c(d)do +for i,a in u(r)do o(i,e); t(e,","); o(a,e); t(e,","); end -y(e); +p(e); t(e,"]"); t(e,","); end if i(a)then t(e,"\"__array\":"); -h(a,e); +d(a,e); t(e,","); end -if r~=#e then y(e);end +if n~=#e then p(e);end t(e,"}"); else -h(a,e); +d(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)); +local o=w(e); +if o=="number"then +t(a,j(e)); elseif o=="string"then -u(e,a); +m(e,a); elseif o=="table"then -local t=b(e); +local t=r(e); if t==f then -h(e,a); +d(e,a); else k(e,a); end @@ -4074,17 +4062,17 @@ function a.encode(t) local e={}; o(t,e); -return p(e); +return v(e); end function a.encode_ordered(t) local e={ordered=true}; o(t,e); -return p(e); +return v(e); end function a.encode_array(t) local e={}; -h(t,e); -return p(e); +d(t,e); +return v(e); end local function o(t,e) return t:find("[^ \t\r\n]",e)or e; @@ -4111,119 +4099,119 @@ end return e; end -local i,h; -local function u(t,e) -local a={}; +local i,r; +local function m(t,e) +local s={}; while true do -local n,s; +local n,a; e=o(t,e+1); if t:byte(e)~=34 then -if t:byte(e)==125 then return a,e+1;end +if t:byte(e)==125 then return s,e+1;end return nil,"key expected"; end -n,e=h(t,e); +n,e=r(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; +a,e=i(t,e+1); +if a==nil then return nil,e;end +s[n]=a; e=o(t,e); local t=t:byte(e); -if t==125 then return d(a),e+1;end +if t==125 then return d(s),e+1;end if t~=44 then return nil,"object eof";end end end -local function c(a,e) +local function u(n,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 +local a; +a,e=i(n,e+1); +if a==nil then +if n:byte(h+1)==93 then return b(s,f),h+2;end +return a,e; +end +t(s,a); +e=o(n,e); +local t=n:byte(e); +if t==93 then return b(s,f),e+1;end if t~=44 then return nil,"array eof";end end end -local s; +local t; 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 e,t=l(e:sub(3,6),16),l(e:sub(9,12),16); +local e=e*1024+t-56613888; 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) +local o=e%64; +e=(e-o)/64; +return h(240+e,128+o,128+t,128+a); +end +local function s(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) +t=true; +end +function r(o,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; +local a=o:find("\"",e,true); +if a then +local e=o:sub(e,a-1); +t=nil; +e=e:gsub("\\u.?.?.?.?",s); +if t then return nil,"invalid escape";end +return e,a+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"; +local function d(t,e) +local t=t:match("[0-9%.%-eE%+]+",e); +return l(t),e+#t; 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 +if o==117 and a==108 and t==108 then +return n,e+4; +end +return nil,"null parse failed"; +end +local function n(t,e) +local a,t,o=t:byte(e+1,e+3); +if a==114 and t==117 and o==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 +local function h(t,e) +local t,a,o,i=t:byte(e+1,e+4); +if t==97 and a==108 and o==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); +function i(a,t) +t=o(a,t); +local e=a:byte(t); +if e==123 then +return m(a,t); +elseif e==91 then +return u(a,t); +elseif e==34 then +return r(a,t); +elseif e~=nil and e>=48 and e<=57 or e==45 then +return d(a,t); +elseif e==110 then +return s(a,t); +elseif e==116 then +return n(a,t); +elseif e==102 then +return h(a,t); else return nil,"value expected"; end @@ -4251,20 +4239,31 @@ 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); +c("FAILED"); +c("encoded:",e); +c("recoded:",t); +else +c(e); end return e==t; end return a; end) package.preload['util.xml']=(function(...) +local _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=require"util.stanza"; local h=require"lxp"; -local e=nil; +a("xml") local e=(function() local n={ ["http://www.w3.org/XML/1998/namespace"]="xml"; @@ -4299,34 +4298,44 @@ function o:CharacterData(e) a:text(e); end -function o:EndElement() +function o:EndElement(e) 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 +local n=h.new(o,"\1"); +local e,i,t,o=n:parse(s); +if e then e,i,t,o=n:parse();end if e then return a.tags[1]; else -return e,n.." (line "..i..", col "..o..")"; +return e,i.." (line "..t..", col "..o..")"; end end; end)(); -return{ parse=e; -}; +return _M; 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={}; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local h=require"util.stanza".stanza; +local t,o=tostring,tonumber; +local n=type; +local s=pairs; +local i='http://jabber.org/protocol/rsm'; +local a={}; do -local e=o; +local e=a; local function t(e) -return i((e:get_text())); +return o((e:get_text())); end local function a(t) return t:get_text(); @@ -4339,62 +4348,73 @@ e.max=t; e.index=t; e.first=function(e) -return{index=i(e.attr.index);e:get_text()}; +return{index=o(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(); +local r=setmetatable({ +first=function(a,e) +if n(e)=="table"then +a:tag("first",{index=e.index}):text(e[1]):up(); +else +a:tag("first"):text(t(e)):up(); end end; -before=function(e,t) -if t==true then +before=function(e,a) +if a==true then e:tag("before"):up(); else -e:tag("before"):text(a(t)):up(); +e:tag("before"):text(t(a)):up(); end end },{ -__index=function(t,e) -return function(o,t) -o:tag(e):text(a(t)):up(); +__index=function(e,o) +return function(e,a) +e:tag(o):text(t(a)):up(); end end; }); +local function t(e) +local t={}; +for o in e:childtags()do +local e=o.name; +local a=e and a[e]; +if a then +t[e]=a(o); +end +end +return t; +end +local function n(t) +local e=h("set",{xmlns=i}); +for t,o in s(t)do +if a[t]then +r[t](e,o); +end +end +return e; +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); +local e=e:get_child("set",i); if e and#e.tags>0 then -return a(e); -end -end -return{parse=a,generate=i,get=t}; +return t(e); +end +end +return{parse=t,generate=n,get=a}; end) package.preload['util.random']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local e=io.open("/dev/urandom","r"); if e then return{ @@ -4406,10 +4426,21 @@ return e.rand; end) package.preload['util.ip']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end 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}; +__eq=function(t,e)return t.addr==e.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 @@ -4431,37 +4462,37 @@ 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); +e,t=e:gsub(":(%d+)%.(%d+)%.(%d+)%.(%d+)$",function(t,e,a,o) +return(":%04X:%04X"):format(t*256+e,a*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; +local function i(a) +local t=""; +local e={}; +if a.proto=="IPv4"then +a=a.toV4mapped; +end +a=(a.addr):upper(); +a:gsub("([^:]*):?",function(t)e[#e+1]=t end); +if not a:match(":$")then e[#e]=nil;end +for o,a in ipairs(e)do +if a:len()==0 and o~=1 and o~=#e then +for e=1,16*(9-#e)do +t=t.."0"; +end +else +for e=1,4-a:len()do +t=t.."0000"; +end +for e=1,a:len()do +t=t..n[a:sub(e,e)]; +end +end +end +return t; end local function t(a,t) a,t=i(a),i(t); @@ -4472,7 +4503,7 @@ end return 128; end -local function s(t) +local function h(t) local e={}; t:gsub("([^.]*).?",function(t)e[#e+1]=tonumber(t)end); if e[1]==127 then @@ -4496,7 +4527,7 @@ return 14; end end -local function n(a) +local function i(a) if t(a,e("::1","IPv6"))==128 then return 0; elseif t(a,e("2002::","IPv6"))>=16 then @@ -4517,7 +4548,7 @@ return 1; end end -local function i(a) +local function n(a) if t(a,e("::1","IPv6"))==128 then return 50; elseif t(a,e("2002::","IPv6"))>=16 then @@ -4538,7 +4569,7 @@ return 40; end end -local function h(o) +local function s(o) local a={}; local t="::ffff:"; o:gsub("([^.]*).?",function(e)a[#a+1]=tonumber(e)end); @@ -4551,16 +4582,16 @@ end function o:toV4mapped() if self.proto~="IPv4"then return nil,"No IPv4 address"end -local e=h(self.addr); +local e=s(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); +e=i(self.toV4mapped); +else +e=i(self); end self.label=e; return e; @@ -4568,9 +4599,9 @@ function o:precedence() local e; if self.proto=="IPv4"then -e=i(self.toV4mapped); -else -e=i(self); +e=n(self.toV4mapped); +else +e=n(self); end self.precedence=e; return e; @@ -4578,126 +4609,111 @@ function o:scope() local e; if self.proto=="IPv4"then -e=s(self.addr); +e=h(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; -} +commonPrefixLength=t}; end) package.preload['util.sasl.scram']=(function(...) -local i,l=require"mime".b64,require"mime".unb64; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local n,u=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))) +local s=require"util.random"; +local d=tonumber; +local h,t=string.char,string.byte; +local i=string.gsub; +local r=a.bxor; +local function l(e,o) +return(i(e,"()(.)",function(a,e) +return h(r(t(e),t(o,a))) 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; +local y,t=e.sha1,e.hmac_sha1; +local function w(o,e,i) +local e=t(o,e.."\0\0\0\1"); +local a=e; for i=2,i do -t=e(o,t); -a=n(a,t); +e=t(o,e); +a=l(a,e); 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 +local function p(e) +return e; +end +local function a(e) +return(i(e,"[,=]",{[","]="=2C",["="]="=3D"})); +end +local function f(e,i) +local a="n="..a(e.username); +local h=n(s.bytes(15)); +local c="r="..h; +local m=a..","..c; +local o=""; +local a=e.conn:ssl()and"y"or"n"; +if i=="SCRAM-SHA-1-PLUS"then +o=e.conn:socket():getfinished(); +a="p=tls-unique"; +end +local s=a..",,"; +local a=s..m; +local a,r=coroutine.yield(a); +if a~="challenge"then return false end +local a,i,f=r:match("(r=[^,]+),s=([^,]*),i=(%d+)"); +local d=d(f); +i=u(i); +if not a or not i or not d then return false,"Could not parse server_first_message"; -elseif a:find(d,3,true)~=3 then +elseif a:find(h,3,true)~=3 then return false,"nonce sent by server does not match our nonce"; -elseif a==r then +elseif a==c 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 o=s..o; +local o="c="..n(o); +local h=o..","..a; +local o; +local a; +local s; +if e.client_key and e.server_key then +a=e.client_key; +s=e.server_key; +else +if e.salted_password then +o=e.salted_password; +elseif e.password then +o=w(p(e.password),i,d); +end +s=t(o,"Server Key"); +a=t(o,"Client Key"); +end +local o=y(a); +local e=m..","..r..","..h; +local o=t(o,e); +local a=l(a,o); +local t=t(s,e); +local e="p="..n(a); +local e=h..","..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 +if u(e)~=t then return false,"server signature did not match"; end return true; @@ -4705,17 +4721,28 @@ 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; +return f,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; +return f,100; end end end end end) package.preload['util.sasl.plain']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end return function(e,t) if t=="PLAIN"and e.username and e.password then return function(e) @@ -4725,6 +4752,17 @@ end end) package.preload['util.sasl.anonymous']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end return function(t,e) if e=="ANONYMOUS"then return function() @@ -4734,6 +4772,17 @@ end end) package.preload['verse.plugins.tls']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local a=require"verse"; local t="urn:ietf:params:xml:ns:xmpp-tls"; function a.plugins.tls(e) @@ -4769,26 +4818,37 @@ end end) package.preload['verse.plugins.sasl']=(function(...) -local n=require"verse"; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local i=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) +function i.plugins.sasl(e) local function r(t) if e.authenticated then return;end e:debug("Authenticating with SASL..."); local t=t:get_child("mechanisms",a); if not t then return end local o={}; -local i={}; +local n={}; for t in t:childtags("mechanism")do t=t:get_text(); e:debug("Server offers %s",t); if not o[t]then -local n=t:match("[^-]+"); -local s,a=pcall(require,"util.sasl."..n:lower()); +local i=t:match("[^-]+"); +local s,a=pcall(require,"util.sasl."..i:lower()); if s then -e:debug("Loaded SASL %s module",n); -o[t],i[t]=a(e,t); +e:debug("Loaded SASL %s module",i); +o[t],n[t]=a(e,t); elseif not tostring(a):match("not found")then e:debug("Loading failed: %s",tostring(a)); end @@ -4803,14 +4863,14 @@ e:close(); return; end -table.sort(t,function(t,e)return i[t]>i[e];end); -local t,i=t[1]; +table.sort(t,function(t,e)return n[t]>n[e];end); +local t,n=t[1]; e:debug("Selecting %s mechanism...",t); e.sasl_mechanism=coroutine.wrap(o[t]); -i=e:sasl_mechanism(t); -local t=n.stanza("auth",{xmlns=a,mechanism=t}); -if i then -t:text(s(i)); +n=e:sasl_mechanism(t); +local t=i.stanza("auth",{xmlns=a,mechanism=t}); +if n then +t:text(s(n)); end e:send(t); return true; @@ -4833,7 +4893,7 @@ e.authenticated=true e:reopen(); else -e:send(n.stanza("response",{xmlns=a}):text(s(t))); +e:send(i.stanza("response",{xmlns=a}):text(s(t))); end return true; end @@ -4843,11 +4903,22 @@ end end) package.preload['verse.plugins.bind']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=require"verse"; -local o=require"util.jid"; +local i=require"util.jid"; local a="urn:ietf:params:xml:ns:xmpp-bind"; function t.plugins.bind(e) -local function i(i) +local function o(o) if e.bound then return;end e:debug("Binding resource..."); e:send_iq(t.iq({type="set"}):tag("bind",{xmlns=a}):tag("resource"):text(e.resource), @@ -4856,7 +4927,7 @@ local t=t :get_child("bind",a) :get_child_text("jid"); -e.username,e.host,e.resource=o.split(t); +e.username,e.host,e.resource=i.split(t); e.jid,e.bound=t,true; e:event("bind-success",{jid=t}); elseif t.attr.type=="error"then @@ -4866,44 +4937,66 @@ end end); end -e:hook("stream-features",i,200); +e:hook("stream-features",o,200); return true; end end) package.preload['verse.plugins.session']=(function(...) -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) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +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) e:debug("Establishing Session..."); -e:send_iq(t.iq({type="set"}):tag("session",{xmlns=a}), +e:send_iq(a.iq({type="set"}):tag("session",{xmlns=o}), function(t) if t.attr.type=="result"then e:event("session-success"); elseif t.attr.type=="error"then -local t,o,a=t:get_error(); -e:event("session-failure",{error=o,text=a,type=t}); +local t,a,o=t:get_error(); +e:event("session-failure",{error=a,text=o,type=t}); end end); return true; end -e:hook("bind-success",o); -end -end -e:hook("stream-features",i); +e:hook("bind-success",i); +end +end +e:hook("stream-features",n); return true; end end) package.preload['verse.plugins.legacy']=(function(...) -local i=require"verse"; -local s=require"util.uuid".generate; -local o="jabber:iq:auth"; -function i.plugins.legacy(e) -local function n(t) -local a=t:get_child("query",o); +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local o=require"verse"; +local n=require"util.uuid".generate; +local i="jabber:iq:auth"; +function o.plugins.legacy(e) +local function s(t) +local a=t:get_child("query",i); 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); @@ -4911,11 +5004,11 @@ local t={ username=e.username; password=e.password; -resource=e.resource or s(); +resource=e.resource or n(); digest=false,sequence=false,token=false; }; -local o=i.iq({to=e.host,type="set"}) -:tag("query",{xmlns=o}); +local o=o.iq({to=e.host,type="set"}) +:tag("query",{xmlns=i}); if#a>0 then for a in a:childtags()do local a=a.name; @@ -4947,52 +5040,63 @@ end end); end -local function t(t) +local function a(t) if not t.version then -e:send_iq(i.iq({type="get"}) +e:send_iq(o.iq({type="get"}) :tag("query",{xmlns="jabber:iq:auth"}) :tag("username"):text(e.username), -n); -end -end -e:hook("opened",t); +s); +end +end +e:hook("opened",a); end end) package.preload['verse.plugins.compression']=(function(...) -local a=require"verse"; -local i=require"zlib"; -local e="http://jabber.org/features/compress" -local t="http://jabber.org/protocol/compress" -local e="http://etherx.jabber.org/streams"; -local e=9; -local function h(o) -local i,e=pcall(i.deflate,e); +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local t=require"verse"; +local e=require"zlib"; +local a="http://jabber.org/features/compress" +local a="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); if i==false then -local t=a.stanza("failure",{xmlns=t}):tag("setup-failed"); +local t=t.stanza("failure",{xmlns=a}):tag("setup-failed"); o:send(t); o:error("Failed to create zlib.deflate filter: %s",tostring(e)); return end return e end -local function d(o) -local i,e=pcall(i.inflate); +local function s(o) +local i,e=pcall(e.inflate); if i==false then -local t=a.stanza("failure",{xmlns=t}):tag("setup-failed"); +local t=t.stanza("failure",{xmlns=a}):tag("setup-failed"); o:send(t); o:error("Failed to create zlib.inflate filter: %s",tostring(e)); return end return e end -local function l(e,o) +local function h(e,o) function e:send(i) local i,o,n=pcall(o,tostring(i),'sync'); if i==false then e:close({ condition="undefined-condition"; text=o; -extra=a.stanza("failure",{xmlns=t}):tag("processing-failed"); +extra=t.stanza("failure",{xmlns=a}):tag("processing-failed"); }); e:warn("Compressed send failed: %s",tostring(o)); return; @@ -5009,7 +5113,7 @@ e:close({ condition="undefined-condition"; text=o; -extra=a.stanza("failure",{xmlns=t}):tag("processing-failed"); +extra=t.stanza("failure",{xmlns=a}):tag("processing-failed"); }); stream:warn("%s",tostring(o)); return; @@ -5017,15 +5121,15 @@ return s(n,o); end; end -function a.plugins.compression(e) -local function i(o) +function t.plugins.compression(e) +local function n(o) if not e.compressed then local o=o:child_with_name("compression"); if o then for o in o:children()do local o=o[1] if o=="zlib"then -e:send(a.stanza("compress",{xmlns=t}):tag("method"):text("zlib")) +e:send(t.stanza("compress",{xmlns=a}):tag("method"):text("zlib")) e:debug("Enabled compression using zlib.") return true; end @@ -5034,49 +5138,60 @@ end end end -local function o(t) -if t.name=="compressed"then +local function o(a) +if a.name=="compressed"then e:debug("Activating compression...") -local t=h(e); +local a=i(e); +if not a then return end +local t=s(e); if not t then return end -local a=d(e); -if not a then return end -l(e,t); -r(e,a); +h(e,a); +r(e,t); e.compressed=true; e:reopen(); -elseif t.name=="failure"then +elseif a.name=="failure"then e:warn("Failed to establish compression"); end end -e:hook("stream-features",i,250); -e:hook("stream/"..t,o); +e:hook("stream-features",n,250); +e:hook("stream/"..a,o); end end) package.preload['verse.plugins.smacks']=(function(...) -local s=require"verse"; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local i=require"verse"; local h=require"socket".gettime; -local n="urn:xmpp:sm:2"; -function s.plugins.smacks(e) +local s="urn:xmpp:sm:3"; +function i.plugins.smacks(e) local t={}; local a=0; local r=h(); local o; -local i=0; -local function l(t) +local n=0; +local function d(t) if t.attr.xmlns=="jabber:client"or not t.attr.xmlns then -i=i+1; -e:debug("Increasing handled stanzas to %d for %s",i,t:top_tag()); -end -end -local function d(a) +n=n+1; +e:debug("Increasing handled stanzas to %d for %s",n,t:top_tag()); +end +end +local function l(a) if a.name and not a.attr.xmlns then t[#t+1]=tostring(a); r=h(); if not o then o=true; e:debug("Waiting to send ack request..."); -s.add_task(1,function() +i.add_task(1,function() if#t==0 then o=false; return; @@ -5087,7 +5202,7 @@ end e:debug("Time up, sending ..."); o=false; -e:send(s.stanza("r",{xmlns=n})); +e:send(i.stanza("r",{xmlns=s})); end); end end @@ -5098,20 +5213,20 @@ if e.resumption_token then e:debug("smacks: have resumption token, reconnecting in 1s..."); e.authenticated=nil; -s.add_task(1,function() +i.add_task(1,function() e:connect(e.connect_host or e.host,e.connect_port or 5222); end); return true; end end -local function r() +local function u() e.resumption_token=nil; e:unhook("disconnected",h); end -local function u(o) +local function r(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)})); +e:debug("Ack requested... acking %d handled stanzas",n); +e:send(i.stanza("a",{xmlns=s,h=tostring(n)})); elseif o.name=="a"then local o=tonumber(o.attr.h); if o>a then @@ -5127,7 +5242,7 @@ elseif o.name=="enabled"then if o.attr.id then e.resumption_token=o.attr.id; -e:hook("closed",r,100); +e:hook("closed",u,100); e:hook("disconnected",h,100); end elseif o.name=="resumed"then @@ -5147,36 +5262,47 @@ e:debug("Resumed successfully"); e:event("resumed"); else -e:warn("Don't know how to handle "..n.."/"..o.name); -end -end -local function o() +e:warn("Don't know how to handle "..s.."/"..o.name); +end +end +local function t() if not e.smacks then e:debug("smacks: sending enable"); -e:send(s.stanza("enable",{xmlns=n,resume="true"})); +e:send(i.stanza("enable",{xmlns=s,resume="true"})); e.smacks=true; -e:hook("stanza",l); -e:hook("outgoing",d); -end -end -local function a(t) -if t:get_child("sm",n)then +e:hook("stanza",d); +e:hook("outgoing",l); +end +end +local function a(a) +if a:get_child("sm",s)then e.stream_management_supported=true; if e.smacks and e.bound then -e:debug("Resuming stream with %d handled stanzas",i); -e:send(s.stanza("resume",{xmlns=n, -h=i,previd=e.resumption_token})); +e:debug("Resuming stream with %d handled stanzas",n); +e:send(i.stanza("resume",{xmlns=s, +h=n,previd=e.resumption_token})); return true; else -e:hook("bind-success",o,1); +e:hook("bind-success",t,1); end end end e:hook("stream-features",a,250); -e:hook("stream/"..n,u); +e:hook("stream/"..s,r); end end) package.preload['verse.plugins.keepalive']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=require"verse"; function t.plugins.keepalive(e) e.keepalive_timeout=e.keepalive_timeout or 300; @@ -5187,16 +5313,28 @@ end end) package.preload['verse.plugins.disco']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local a=require"verse"; -local r=require("mime").b64; -local h=require("util.hashes").sha1; +local e=require("mime").b64; +local e=require("util.hashes").sha1; +local r=require"util.caps".calculate_hash; local s="http://jabber.org/protocol/caps"; local e="http://jabber.org/protocol/disco"; local i=e.."#info"; local o=e.."#items"; function a.plugins.disco(e) e:add_plugin("presence"); -local t={ +local n={ __index=function(t,e) local a={identities={},features={}}; if e=="identities"or e=="features"then @@ -5206,10 +5344,10 @@ return a; end, }; -local n={ -__index=function(t,a) +local t={ +__index=function(a,t) local e={}; -t[a]=e; +a[t]=e; return e; end, }; @@ -5226,56 +5364,32 @@ [o]=true, }, }, -},t); -items=setmetatable({[false]={}},n); +},n); +items=setmetatable({[false]={}},t); }; e.caps={} e.caps.node='http://code.matthewwild.co.uk/verse/' -local function n(t,e) -if t.category0 then +local a={}; +for t,e in pairs(self.s5b_peer_candidates or{})do +a[#a+1]=e; +end +if#a>0 then self.connecting_peer_candidates=true; -local function s(t,e) -self.jingle:send_command("transport-info",a.stanza("content",{creator=self.creator,name=self.name}) +local function s(e,a) +self.jingle:send_command("transport-info",t.stanza("content",{creator=self.creator,name=self.name}) :tag("transport",{xmlns=o,sid=self.s5b_sid}) -:tag("candidate-used",{cid=t.cid})); +:tag("candidate-used",{cid=e.cid})); self.onconnect_callback=i; -self.conn=e; -end -local e=n(self.s5b_sid..self.peer..e.jid,true); -h(s,t,e); +self.conn=a; +end +local e=h(self.s5b_sid..self.peer..e.jid,true); +n(s,a,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 t:info_received(t) +function a:info_received(a) e:warn("Info received"); -local s=t:child_with_name("content"); +local s=a: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 t=i:child_with_name("candidate-used"); -if t then -local function d(i,e) +local a=i:child_with_name("candidate-used"); +if a then +local function i(i,e) if self.jingle.role=="initiator"then -self.jingle.stream:send_iq(a.iq({to=i.jid,type="set"}) +self.jingle.stream:send_iq(t.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",a.stanza("content",s.attr) +self.jingle:send_command("transport-info",t.stanza("content",s.attr) :tag("transport",{xmlns=o,sid=self.s5b_sid}) -:tag("activated",{cid=t.attr.cid})); +:tag("activated",{cid=a.attr.cid})); self.conn=e; self.onconnect_callback(e); else @@ -6120,25 +6304,25 @@ end); end end -self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[t.attr.cid]); +self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]); local t={ -self.jingle.stream.proxy65.available_streamhosts[t.attr.cid]; +self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]; }; -local e=n(self.s5b_sid..e.jid..self.peer,true); -h(d,t,e); +local e=h(self.s5b_sid..e.jid..self.peer,true); +n(i,t,e); end elseif i:get_child("activated")then self.onconnect_callback(self.conn); end end -function t:disconnect() +function a:disconnect() if self.conn then self.conn:close(); end end -function t:handle_accepted(e) -end -local t={__index=t}; +function a:handle_accepted(e) +end +local t={__index=a}; e:hook("jingle/transport/"..o,function(e) return setmetatable({ role=e.role, @@ -6150,8 +6334,19 @@ end end) package.preload['verse.plugins.proxy65']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local a=require"verse"; -local h=require"util.uuid"; +local d=require"util.uuid"; local r=require"util.hashes".sha1; local n={}; n.__index=n; @@ -6222,31 +6417,31 @@ t:event("proxy65/request",e); end); end -function n:new(t,n) +function n:new(t,h) local e=a.new(nil,{ target_jid=t; -bytestream_sid=h.generate(); +bytestream_sid=d.generate(); }); 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 +for t,e in ipairs(h 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}); +local a,t,o=o:get_error(); +e:event("connection-failed",{conn=e,type=a,condition=t,text=o}); else 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 +local n,o; +for a,t in ipairs(h or self.proxies)do if t.jid==e.streamhost_jid then -o,h=t.host,t.port; +n,o=t.host,t.port; break; end end -e:connect(o,h); +e:connect(n,o); local function o() e:unhook("connected",o); local t=a.iq{to=e.streamhost_jid,type="set"} @@ -6265,10 +6460,10 @@ end); return e; end -function s(i,e,t,o,a) -local t=r(t..o..a); -local function i() -e:unhook("connected",i); +function s(i,e,a,t,o) +local a=r(a..t..o); +local function t() +e:unhook("connected",t); return true; end local function o(t) @@ -6279,25 +6474,36 @@ e:event("connected"); return true; end -local function a(i) -e:unhook("incoming-raw",a); -if i~="\005\000"then -local t="version-mismatch"; -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,#t)..t.."\0\0"); +local function i(t) +e:unhook("incoming-raw",i); +if t~="\005\000"then +local a="version-mismatch"; +if t:sub(1,1)=="\005"then +a="authentication-failure"; +end +return e:event("error",a); +end +e:send(string.char(5,1,0,3,#a)..a.."\0\0"); e:hook("incoming-raw",o,100); return true; end -e:hook("connected",i,200); -e:hook("incoming-raw",a,100); +e:hook("connected",t,200); +e:hook("incoming-raw",i,100); e:send("\005\001\000"); end end) package.preload['verse.plugins.jingle_ibb']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local e=require"verse"; local i=require"util.encodings".base64; local s=require"util.uuid".generate; @@ -6313,11 +6519,11 @@ t=e.eventable(t); return t; end -function a:initiate(a,e,t) +function a:initiate(e,t,a) self.block=2048; -self.stanza=t or'iq'; -self.peer=a; -self.sid=e or tostring(self):match("%x+$"); +self.stanza=a or'iq'; +self.peer=e; +self.sid=t or tostring(self):match("%x+$"); self.iseq=0; self.oseq=0; local e=function(e) @@ -6325,10 +6531,10 @@ end self.feeder=e; print("Hooking incomming IQs"); -local a=self.stream; -a:hook("iq/"..o,e) -if t=="message"then -a:hook("message",e) +local t=self.stream; +t:hook("iq/"..o,e) +if a=="message"then +t:hook("message",e) end end function a:open(t) @@ -6476,15 +6682,26 @@ end end) package.preload['verse.plugins.pubsub']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local i=require"verse"; -local h=table.insert; +local n=table.insert; local o="http://jabber.org/protocol/pubsub"; -local s="http://jabber.org/protocol/pubsub#owner"; +local h="http://jabber.org/protocol/pubsub#owner"; local a="http://jabber.org/protocol/pubsub#event"; local e={}; -local n={__index=e}; +local s={__index=e}; function i.plugins.pubsub(e) -e.pubsub=setmetatable({stream=e},n); +e.pubsub=setmetatable({stream=e},s); e:hook("message",function(t) local o=t.attr.from; for t in t:childtags("event",a)do @@ -6503,32 +6720,32 @@ end); return true; end -function e:create(e,t,a) -return self:service(e):node(t):create(nil,a); -end -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); +function e:create(e,a,t) +return self:service(e):node(a):create(nil,t); +end +function e:subscribe(a,e,o,t) +return self:service(a):node(e):subscribe(o,nil,t); +end +function e:publish(e,a,o,i,t) +return self:service(e):node(a):publish(o,nil,i,t); end local a={}; local t={__index=a}; function e:service(e) return setmetatable({stream=self.stream,service=e},t) end -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 +local function t(h,e,r,a,s,n,t) +local e=i.iq{type=h or"get",to=e} +:tag("pubsub",{xmlns=r or o}) +if a then e:tag(a,{node=s,jid=n});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(i) -if i.attr.type=="result"then -local e=i:get_child("pubsub",o); +,a and function(t) +if t.attr.type=="result"then +local e=t:get_child("pubsub",o); local e=e and e:get_child("subscriptions"); local o={}; if e then @@ -6536,32 +6753,32 @@ local e=self:node(t.attr.node) e.subscription=t; e.subscribed_jid=t.attr.jid; -h(o,e); +n(o,e); end end a(o); else -a(false,i:get_error()); +a(false,t:get_error()); end end or nil); end -function a:affiliations(e) +function a:affiliations(a) self.stream:send_iq(t(nil,self.service,nil,"affiliations") -,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()); +,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 e in e:childtags("affiliation")do +local a=self:node(e.attr.node) +a.affiliation=e; +n(t,a); +end +end +a(t); +else +a(false,e:get_error()); end end or nil); end @@ -6580,7 +6797,7 @@ function a:node(e) return setmetatable({stream=self.stream,service=self.service,node=e},o) end -function n:__call(t,e) +function s:__call(t,e) local t=self:service(t); return e and t:node(e)or t; end @@ -6605,11 +6822,11 @@ end end end -function e:create(e,a) -if e~=nil then +function e:create(a,e) +if a~=nil then error("Not implemented yet."); else -self.stream:send_iq(t("set",self.service,nil,"create",self.node),a); +self.stream:send_iq(t("set",self.service,nil,"create",self.node),e); end end function e:configure(e,a) @@ -6618,13 +6835,13 @@ end self.stream:send_iq(t("set",self.service,nil,e==nil and"default"or"configure",self.node),a); end -function e:publish(i,a,e,o) -if a~=nil then +function e:publish(a,e,o,i) +if e~=nil then error("Node configuration is not implemented yet."); end -self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,i or true) -:add_child(e) -,o); +self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,a or true) +:add_child(o) +,i); end function e:subscribe(e,o,a) e=e or self.stream.jid; @@ -6660,22 +6877,33 @@ 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); +function e:retract(a,e) +self.stream:send_iq(t("set",self.service,nil,"retract",self.node,nil,a) +,e); end function e:purge(a,e) assert(not a,"Not implemented yet."); -self.stream:send_iq(t("set",self.service,s,"purge",self.node) +self.stream:send_iq(t("set",self.service,h,"purge",self.node) ,e); end function e:delete(a,e) assert(not a,"Not implemented yet."); -self.stream:send_iq(t("set",self.service,s,"delete",self.node) +self.stream:send_iq(t("set",self.service,h,"delete",self.node) ,e); end end) package.preload['verse.plugins.pep']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local e=require"verse"; local t="http://jabber.org/protocol/pubsub"; local t=t.."#event"; @@ -6700,12 +6928,23 @@ e:remove_disco_feature(t.."+notify"); end end -function e:publish_pep(t,a) -return e.pubsub:service(nil):node(a or t.attr.xmlns):publish(nil,nil,t) +function e:publish_pep(t,a,o) +return e.pubsub:service(nil):node(a or t.attr.xmlns):publish(o or"current",nil,t) end end end) package.preload['verse.plugins.adhoc']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local o=require"verse"; local n=require"lib.adhoc"; local t="http://jabber.org/protocol/commands"; @@ -6717,24 +6956,24 @@ e:add_plugin("disco"); e:add_disco_feature(t); function e:query_commands(a,o) -e:disco_items(a,t,function(t) +e:disco_items(a,t,function(a) e:debug("adhoc list returned") -local a={}; -for o,t in ipairs(t)do -a[t.node]=t.name; +local t={}; +for o,a in ipairs(a)do +t[a.node]=a.name; end e:debug("adhoc calling callback") -return o(a); +return o(t); end); end -function e:execute_command(i,o,t) +function e:execute_command(t,i,o) local e=setmetatable({ -stream=e,jid=i, -command=o,callback=t +stream=e,jid=t, +command=i,callback=o },a); return e:execute(); end -local function s(t,e) +local function h(t,e) if not(e)or e=="user"then return true;end if type(e)=="function"then return e(t); @@ -6745,24 +6984,24 @@ e:add_disco_item({jid=e.jid,node=a,name=o},t); return i[a]; end -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") +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 h(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") :tag("note",{type="error"}):text("You don't have permission to execute this command"))); return true end -return n.handle_cmd(a,{send=function(t)return e:send(t)end},t); +return n.handle_cmd(t,{send=function(t)return e:send(t)end},a); end e:hook("iq/"..t,function(e) -local t=e.attr.type; -local a=e.tags[1].name; -if t=="set"and a=="command"then -return h(e); +local a=e.attr.type; +local t=e.tags[1].name; +if a=="set"and t=="command"then +return s(e); end end); end @@ -6786,20 +7025,31 @@ self:_process_response(e); end); end -function a:next(a) -local e=o.iq({to=self.jid,type="set"}) +function a:next(e) +local t=o.iq({to=self.jid,type="set"}) :tag("command",{ xmlns=t, node=self.command, sessionid=self.sessionid }); -if a then e:add_child(a);end -self.stream:send_iq(e,function(e) +if e then t:add_child(e);end +self.stream:send_iq(t,function(e) self:_process_response(e); end); end end) package.preload['verse.plugins.presence']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local a=require"verse"; function a.plugins.presence(t) t.last_presence=nil; @@ -6809,8 +7059,8 @@ end end,1); function t:resend_presence() -if last_presence then -t:send(last_presence); +if self.last_presence then +t:send(self.last_presence); end end function t:set_status(e) @@ -6819,42 +7069,55 @@ 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 +if e.priority or e.prio then +a:tag("priority"):text(tostring(e.priority or e.prio)):up(); +end +if e.status or e.msg then +a:tag("status"):text(e.status or e.msg):up(); +end +elseif type(e)=="string"then +a:tag("status"):text(e):up(); end t:send(a); end end end) package.preload['verse.plugins.private']=(function(...) -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}); +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local a=require"verse"; +local t="jabber:iq:private"; +function a.plugins.private(o) +function o:private_set(i,o,e,n) +local t=a.iq({type="set"}) +:tag("query",{xmlns=t}); if e then -if e.name==o and e.attr and e.attr.xmlns==i then +if e.name==i and e.attr and e.attr.xmlns==o then t:add_child(e); else -t:tag(o,{xmlns=i}) +t:tag(i,{xmlns=o}) :add_child(e); end end self:send_iq(t,n); end -function o:private_get(e,o,i) -self:send_iq(t.iq({type="get"}) -:tag("query",{xmlns=a}) -:tag(e,{xmlns=o}), -function(t) -if t.attr.type=="result"then -local t=t:get_child("query",a); -local e=t:get_child(e,o); +function o:private_get(o,e,i) +self:send_iq(a.iq({type="get"}) +:tag("query",{xmlns=t}) +:tag(o,{xmlns=e}), +function(a) +if a.attr.type=="result"then +local t=a:get_child("query",t); +local e=t:get_child(o,e); i(e); end end); @@ -6862,13 +7125,24 @@ end end) package.preload['verse.plugins.roster']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local i=require"verse"; -local d=require"util.jid".bare; +local l=require"util.jid".bare; local a="jabber:iq:roster"; local o="urn:xmpp:features:rosterver"; local n=table.insert; function i.plugins.roster(t) -local r=false; +local h=false; local e={ items={}; ver=""; @@ -6876,7 +7150,7 @@ t.roster=e; t:hook("stream-features",function(e) if e:get_child("ver",o)then -r=true; +h=true; end end); local function s(t) @@ -6892,17 +7166,17 @@ end return e; end -local function l(o) +local function d(a) local e={}; -local a={}; -e.groups=a; -for t,a in pairs(o.attr)do +local t={}; +e.groups=t; +for t,a in pairs(a.attr)do if t~="xmlns"then e[t]=a end end -for e in o:childtags("group")do -n(a,e:get_text()) +for e in a:childtags("group")do +n(t,e:get_text()) end return e; end @@ -6915,8 +7189,8 @@ items=e.items, }; end -function e:add_contact(o,n,h,e) -local o={jid=o,name=n,groups=h}; +function e:add_contact(n,o,h,e) +local o={jid=n,name=o,groups=h}; local a=i.iq({type="set"}) :tag("query",{xmlns=a}) :add_child(s(o)); @@ -6945,24 +7219,24 @@ end end); end -local function h(t) -local t=l(t); +local function r(t) +local t=d(t); e.items[t.jid]=t; end -local function l(t) +local function d(t) local a=e.items[t]; e.items[t]=nil; return a; end function e:fetch(o) -t:send_iq(i.iq({type="get"}):tag("query",{xmlns=a,ver=r and e.ver or nil}), +t:send_iq(i.iq({type="get"}):tag("query",{xmlns=a,ver=h and e.ver or nil}), function(t) if t.attr.type=="result"then local t=t:get_child("query",a); if t then e.items={}; for t in t:childtags("item")do -h(t) +r(t) end e.ver=t.attr.ver or""; end @@ -6972,41 +7246,52 @@ end end); end -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"); +t:hook("iq/"..a,function(o) +local s,n=o.attr.type,o.attr.from; +if s=="set"and(not n or n==l(t.jid))then +local n=o:get_child("query",a); +local a=n and n:get_child("item"); +if a then +local i,o; +local s=a.attr.jid; +if a.attr.subscription=="remove"then +i="removed" +o=d(s); +else +i=e.items[s]and"changed"or"added"; +r(a) +o=e.items[s]; +end +e.ver=n.attr.ver; if o then -local n,a; -local i=o.attr.jid; -if o.attr.subscription=="remove"then -n="removed" -a=l(i); -else -n=e.items[i]and"changed"or"added"; -h(o) -a=e.items[i]; -end -e.ver=s.attr.ver; -if a then -t:event("roster/item-"..n,a); -end -end -t:send(i.reply(n)) +t:event("roster/item-"..i,o); +end +end +t:send(i.reply(o)) return true; end end); end end) package.preload['verse.plugins.register']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=require"verse"; -local o="jabber:iq:register"; +local i="jabber:iq:register"; function t.plugins.register(e) -local function a(i) -if i:get_child("register","http://jabber.org/features/iq-register")then +local function a(o) +if o:get_child("register","http://jabber.org/features/iq-register")then local t=t.iq({to=e.host_,type="set"}) -:tag("query",{xmlns=o}) +:tag("query",{xmlns=i}) :tag("username"):text(e.username):up() :tag("password"):text(e.password):up(); if e.register_email then @@ -7032,8 +7317,19 @@ end end) package.preload['verse.plugins.groupchat']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local i=require"verse"; -local e=require"events"; +local e=require"util.events"; local n=require"util.jid"; local a={}; a.__index=a; @@ -7051,17 +7347,17 @@ end if t and t.opts.source and e.attr.to~=t.opts.source then return end if t then -local i=select(3,n.split(e.attr.from)); +local o=select(3,n.split(e.attr.from)); local n=e:get_child_text("body"); -local o=e:get_child("delay",h); +local i=e:get_child("delay",h); local a={ room_jid=a; room=t; -sender=t.occupants[i]; -nick=i; +sender=t.occupants[o]; +nick=o; body=n; stanza=e; -delay=(o and o.attr.stamp); +delay=(i and i.attr.stamp); }; local t=t:event(e.name,a); return t or(e.name=="message")or nil; @@ -7175,17 +7471,17 @@ end self:send(e); end -function a:admin_set(e,a,t,o) +function a:admin_set(a,t,o,e) self:send(i.iq({type="set"}) :query(s.."#admin") -:tag("item",{nick=e,[a]=t}) -:tag("reason"):text(o or"")); -end -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); +:tag("item",{nick=a,[t]=o}) +:tag("reason"):text(e or"")); +end +function a:set_role(t,e,a) +self:admin_set(t,"role",e,a); +end +function a:set_affiliation(a,e,t) +self:admin_set(a,"affiliation",e,t); end function a:kick(e,t) self:set_role(e,"none",t); @@ -7195,6 +7491,17 @@ end end) package.preload['verse.plugins.vcard']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local i=require"verse"; local o=require"util.vcard"; local e="vcard-temp"; @@ -7229,6 +7536,17 @@ end end) package.preload['verse.plugins.vcard_update']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local n=require"verse"; local i="vcard-temp:x:update"; local s=require("util.hashes").sha1; @@ -7248,16 +7566,16 @@ e:add_plugin("vcard"); e:add_plugin("presence"); local t; -local function r(o) -local a; -for e=1,#o do -if o[e].name=="PHOTO"then -a=o[e][1]; +local function r(a) +local o; +for e=1,#a do +if a[e].name=="PHOTO"then +o=a[e][1]; break end end -if a then -local a=s(h(a),true); +if o then +local a=s(h(o),true); t=n.stanza("x",{xmlns=i}) :tag("photo"):text(a); e:resend_presence() @@ -7285,19 +7603,30 @@ end end) package.preload['verse.plugins.carbons']=(function(...) -local o=require"verse"; -local a="urn:xmpp:carbons:2"; -local r="urn:xmpp:forward:0"; -local h=os.time; -local s=require"util.datetime".parse; -local n=require"util.jid".bare; -function o.plugins.carbons(e) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local a=require"verse"; +local o="urn:xmpp:carbons:2"; +local n="urn:xmpp:forward:0"; +local s=os.time; +local r=require"util.datetime".parse; +local h=require"util.jid".bare; +function a.plugins.carbons(e) local t={}; t.enabled=false; e.carbons=t; function t:enable(i) -e:send_iq(o.iq{type="set"} -:tag("enable",{xmlns=a}) +e:send_iq(a.iq{type="set"} +:tag("enable",{xmlns=o}) ,function(e) local e=e.attr.type=="result"; if e then @@ -7309,8 +7638,8 @@ end or nil); end function t:disable(i) -e:send_iq(o.iq{type="set"} -:tag("disable",{xmlns=a}) +e:send_iq(a.iq{type="set"} +:tag("disable",{xmlns=o}) ,function(e) local e=e.attr.type=="result"; if e then @@ -7321,24 +7650,24 @@ end end or nil); end -local o; +local i; e:hook("bind-success",function() -o=n(e.jid); +i=h(e.jid); end); -e:hook("message",function(i) -local t=i:get_child(nil,a); -if i.attr.from==o and t then +e:hook("message",function(a) +local t=a:get_child(nil,o); +if a.attr.from==i and t then local o=t.name; -local t=t:get_child("forwarded",r); +local t=t:get_child("forwarded",n); 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; -t=t and s(t); +t=t and r(t); if a then return e:event("carbon",{ dir=o, stanza=a, -timestamp=t or h(), +timestamp=t or s(), }); end end @@ -7346,75 +7675,85 @@ end end) package.preload['verse.plugins.archive']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end 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 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 m=i{ +local e="urn:xmpp:mam:2" +local n="urn:xmpp:forward:0"; +local c="urn:xmpp:delay"; +local d=require"util.uuid".generate; +local m=require"util.datetime".parse; +local s=require"util.datetime".datetime; +local o=require"util.dataforms".new; +local h=require"util.rsm"; +local l={}; +local u=o{ {name="FORM_TYPE";type="hidden";value=e;}; {name="with";type="jid-single";}; {name="start";type="text-single"}; {name="end";type="text-single";}; }; -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 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); +function a.plugins.archive(i) +function i:query_archive(o,a,r) +local d=d(); +local o=t.iq{type="set",to=o} +:tag("query",{xmlns=e,queryid=d}); +local i,t=tonumber(a["start"]),tonumber(a["end"]); +a["start"]=i and s(i); +a["end"]=t and s(t); +o:add_child(u:form(a,"submit")); +o:add_child(h.generate(a)); +local t={}; +local function i(o) +local a=o:get_child("result",e); +if a and a.attr.queryid==d then +local e=a:get_child("forwarded",n); +e=e or o:get_child("forwarded",n); +local o=a.attr.id; +local a=e:get_child("delay",c); +local a=a and m(a.attr.stamp)or nil; +local e=e:get_child("message","jabber:client") +t[#t+1]={id=o,stamp=a,message=e}; return true end -local e=o:get_child("result",e); -if e and e.attr.queryid==i then -local t=e:get_child("forwarded",h); -t=t or o:get_child("forwarded",h); -local o=e.attr.id; -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 -self:hook("message",n,1); -self:send_iq(o,function(e) -if e.attr.type=="error"then -self:warn(table.concat({e:get_error()}," ")) -self:unhook("message",n); -s(false,e:get_error()) -end +end +self:hook("message",i,1); +self:send_iq(o,function(a) +self:unhook("message",i); +if a.attr.type=="error"then +self:warn(table.concat({a:get_error()}," ")) +r(false,a:get_error()) +return true; +end +local e=a:get_child("fin",e) +if e then +local e=h.get(e); +for a,e in pairs(e or l)do t[a]=e;end +end +r(t); return true end); end -local i={ +local n={ always=true,[true]="always", never=false,[false]="never", roster="roster", } -local function h(t) +local function s(t) local e={}; local a=t.attr.default; if a then -e[false]=i[a]; +e[false]=n[a]; end local a=t:get_child("always"); if a then @@ -7432,11 +7771,11 @@ end return e; end -local function n(o) +local function h(o) local a a,o[false]=o[false],nil; if a~=nil then -a=i[a]; +a=n[a]; end local i=t.stanza("prefs",{xmlns=e,default=a}) local a=t.stanza("always"); @@ -7446,31 +7785,42 @@ end return i:add_child(a):add_child(e); end -function s:archive_prefs_get(a) +function i: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 t=h(e.tags[1]); +local t=s(e.tags[1]); a(t,e); else a(nil,e); end end); end -function s:archive_prefs_set(a,e) -self:send_iq(t.iq{type="set"}:add_child(n(a)),e); +function i:archive_prefs_set(e,a) +self:send_iq(t.iq{type="set"}:add_child(h(e)),a); end 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) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local t,n=string.format,string.char; +local o,s,h=pairs,ipairs,tonumber; +local i,d=table.insert,table.concat; +local function r(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)); +return e and(e:gsub("%%(%x%x)",function(e)return n(h(e,16));end)); end local function e(e) return e and(e:gsub("%W",function(e) @@ -7481,47 +7831,57 @@ end end)); end -local function h(t) +local function n(t) local a={}; if t[1]then -for i,t in s(t)do -o(a,e(t.name).."="..e(t.value)); -end -else -for t,i in r(t)do -o(a,e(t).."="..e(i)); +for o,t in s(t)do +i(a,e(t.name).."="..e(t.value)); +end +else +for o,t in o(t)do +i(a,e(o).."="..e(t)); end end 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=a(t),a(e); -o(i,{name=t,value=e}); -i[t]=e; -end -return i; -end -local function t(e,t) +local o={}; +for e,t in e:gmatch("([^=&]*)=([^&]*)")do +e,t=e:gsub("%+","%%20"),t:gsub("%+","%%20"); +e,t=a(e),a(t); +i(o,{name=e,value=t}); +o[e]=t; +end +return o; +end +local function o(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; +urlencode=r,urldecode=a; +formencode=n,formdecode=s; +contains_token=o; }; end) package.preload['net.http.parser']=(function(...) -local c=tonumber; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local m=tonumber; local a=assert; -local g,u=table.insert,table.concat; -local q=require"socket.url".parse; +local v=require"socket.url".parse; local t=require"util.http".urldecode; -local function k(e) +local function b(e) e=t((e:gsub("//+","/"))); if e:sub(1,1)~="/"then e="/"..e; @@ -7539,154 +7899,132 @@ 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 y={}; +function y.new(c,h,e,y) +local d=true; +if not e or e=="server"then d=false;else a(e=="client","Invalid parser type");end +local e=""; +local p,o,r; +local s=nil; local t; -local y; -local s; +local a; +local u; +local n; 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); +feed=function(l,i) +if n then return nil,"parse has failed";end +if not i then +if s and d and not a then +t.body=e; +c(t); elseif e~=""then -s=true;return r("unexpected-eof"); +n=true;return h(); 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 +e=e..i; +while#e>0 do +if s==nil then 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={}; +local w,r,l,i,g; +local c; +local o={}; for t in e:sub(1,f+1):gmatch("([^\r\n]+)\r\n")do -if p then +if c then local e,t=t:match("^([^%s:]+): *(.*)$"); -if not e then s=true;return r("invalid-header-line");end +if not e then n=true;return h("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") +o[e]=o[e]and o[e]..","..t or t; +else +c=t; +if d then +l,i,g=t:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); +i=m(i); +if not i then n=true;return h("invalid-status-line");end +u=not +((y and y().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={ +w,r,l=t:match("^(%w+) (%S+) HTTP/(1%.[01])$"); +if not w then n=true;return h("invalid-status-line");end +end +end +end +if not c then n=true;return h("invalid-status-line");end +p=u and o["transfer-encoding"]=="chunked"; +a=m(o["content-length"]); +if d then +if not u then a=0;end +t={ code=i; -httpversion=u; -headers=a; -body=y and""or nil; -responseversion=u; -responseheaders=a; +httpversion=l; +headers=o; +body=u and""or nil; +responseversion=l; +responseheaders=o; }; else local e; -if h:byte()==47 then -local a,t=h:match("([^?]*).?(.*)"); +if r:byte()==47 then +local a,t=r: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={ +e=v(r); +if not(e and e.path)then n=true;return h("invalid-url");end +end +r=b(e.path); +o.host=e.host or o.host; +a=a or 0; +t={ method=w; url=e; -path=h; -httpversion=u; -headers=a; +path=r; +httpversion=l; +headers=o; body=nil; }; end e=e:sub(f+4); -o=#e; -d=true; -end +s=true; +end +if s then 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; +c(t); +elseif#e-r-2>=o then +t.body=t.body..e:sub(r,r+(o-1)); +e=e:sub(r+o+2); +o,r=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); +elseif a and#e>=a then +if t.code==101 then +t.body,e=e,""; +else +t.body,e=e:sub(1,a),e:sub(a+1); +end +s=nil;c(t); 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); +elseif#e>=a then +t.body,e=e:sub(1,a),e:sub(a+1); +s=nil;c(t); else break; end @@ -7695,35 +8033,45 @@ end; }; end -return v; +return y; 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 _ENV=_ENV; +local function a(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local g=require"socket" +local b=require"util.encodings".base64.encode; +local l=require"socket.url" +local u=require"net.http.parser".new; +local s=require"util.http"; +local k=pcall(require,"ssl"); +local q=require"net.server" +local d,o=table.insert,table.concat; +local m=pairs; +local y,h,w,f,r= +tonumber,tostring,xpcall,select,debug.traceback; +local p,v=assert,error +local c=require"util.logger".init("http"); +a"http" +local i={}; +local n={default_port=80,default_mode="*a"}; +function n.onconnect(t) +local e=i[t]; local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"}; if e.query then -i(a,4,"?"..e.query); +d(a,4,"?"..e.query); end t:write(o(a)); local a={[2]=": ",[4]="\r\n"}; -for e,i in w(e.headers)do +for e,i in m(e.headers)do a[1],a[3]=e,i; t:write(o(a)); end @@ -7732,185 +8080,152 @@ t:write(e.body); end end -function r.onincoming(t,a) -local e=n[t]; +function n.onincoming(a,t) +local e=i[a]; if not e then -d("warn","Received response from connection %s with no request attached!",l(t)); +c("warn","Received response from connection %s with no request attached!",h(a)); return; end -if a and e.reader then -e:reader(a); -end -end -function r.ondisconnect(t,a) -local e=n[t]; +if t and e.reader then +e:reader(t); +end +end +function n.ondisconnect(t,a) +local e=i[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) +e:reader(nil,a); +end +i[t]=nil; +end +function n.ondetach(e) +i[e]=nil; +end +local function x(e,a,i) if not e.parser then -local function a(t) +local function o(t) if e.callback then e.callback(t or"connection-closed",0,e); e.callback=nil; end -o(e); -end -if not i then -a(n); +destroy_request(e); +end +if not a then +o(i); return; end -local function i(t) +local function a(t) if e.callback then e.callback(t.body,t.code,t,e); e.callback=nil; end -o(e); +destroy_request(e); end local function t() return e; end -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; +e.parser=u(a,o,"client",t); +end +e.parser:feed(a); +end +local function j(e)c("error","Traceback[http]: %s",r(h(e),2));end +function request(e,t,r) +local e=l.parse(e); if not(e and e.host)then -h("invalid-url",0,e); +r(nil,0,e); return nil,"invalid-url"; end if not e.path then e.path="/"; end -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; +local l,o,s; +local u,a=e.host,e.port; +local d=u; +if(a=="80"and e.scheme=="http") +or(a=="443"and e.scheme=="https")then +a=nil; +elseif a then +d=d..":"..a; +end +o={ +["Host"]=d; ["User-Agent"]="Prosody XMPP Server"; }; if e.userinfo then -s["Authorization"]="Basic "..z(e.userinfo); +o["Authorization"]="Basic "..b(e.userinfo); end if t then e.onlystatus=t.onlystatus; -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 +s=t.body; +if s then +l="POST"; +o["Content-Length"]=h(#s); +o["Content-Type"]="application/x-www-form-urlencoded"; +end +if t.method then l=t.method;end if t.headers then -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 +for e,t in m(t.headers)do +o[e]=t; +end +end +end +e.method,e.headers,e.body=l,o,s; +local o=e.scheme=="https"; +if o and not k then +v("SSL not available, unable to contact https URL"); +end +local h=a and y(a)or(o and 443 or 80); +local a=g.tcp(); +a:settimeout(10); +local d,s=a:connect(u,h); +if not d and s~="timeout"then +r(nil,0,e); +return nil,s; +end +local s=false; +if o then +s=t and t.sslctx or{mode="client",protocol="sslv23",options={"no_sslv2","no_sslv3"}}; +end +e.handler,e.conn=p(q.wrapclient(a,u,h,n,"*a",s)); e.write=function(...)return e.handler:write(...);end -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.callback=function(i,t,o,a)c("debug","Calling callback, status %s",t or"---");return f(2,w(function()return r(i,t,o,a)end,j));end e.reader=x; e.state="status"; -n[e.handler]=e; -o.events.fire_event("request",{http=o,request=e,url=a}); -return e; -end -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; -}; +i[e.handler]=e; +return e; +end +function destroy_request(e) +if e.conn then +e.conn=nil; +e.handler:close() +end +end +local e,t=s.urlencode,s.urldecode; +local o,a=s.formencode,s.formdecode; +_M.urlencode,_M.urldecode=e,t; +_M.formencode,_M.formdecode=o,a; +return _M; end) package.preload['verse.bosh']=(function(...) -local r=require"util.xmppstream".new; -local h=require"util.stanza"; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local h=require"util.xmppstream".new; +local i=require"util.stanza"; require"net.httpclient_listener"; local o=require"net.http"; local e=setmetatable({},{__index=verse.stream_mt}); e.__index=e; -local s="http://etherx.jabber.org/streams"; -local n="http://jabber.org/protocol/httpbind"; -local i=5; +local n="http://etherx.jabber.org/streams"; +local s="http://jabber.org/protocol/httpbind"; +local a=5; function verse.new_bosh(a,t) local t={ bosh_conn_pool={}; @@ -7932,7 +8247,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]=h.clone(e); +self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1]=i.clone(e); self:flush(); end function e:flush() @@ -7944,20 +8259,20 @@ self:debug("Flushing..."); 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; +for a,o in ipairs(t)do +e:add_child(o); +t[a]=nil; end self:_make_request(e); else self:debug("Decided not to flush."); end end -function e:_make_request(t) -local e,t=o.request(self.bosh_url,{body=tostring(t)},function(o,e,a) +function e:_make_request(i) +local e,t=o.request(self.bosh_url,{body=tostring(i)},function(o,e,t) if e~=0 then self.inactive_since=nil; -return self:_handle_response(o,e,a); +return self:_handle_response(o,e,t); end local e=os.time(); if not self.inactive_since then @@ -7966,17 +8281,17 @@ return self:_disconnected(); else self:debug("%d seconds left to reconnect, retrying in %d seconds...", -self.bosh_max_inactivity-(e-self.inactive_since),i); -end -timer.add_task(i,function() +self.bosh_max_inactivity-(e-self.inactive_since),a); +end +timer.add_task(a,function() self:debug("Retrying request..."); -for e,t in ipairs(self.bosh_waiting_requests)do -if t==a then +for e,a in ipairs(self.bosh_waiting_requests)do +if a==t then table.remove(self.bosh_waiting_requests,e); break; end end -self:_make_request(t); +self:_make_request(i); end); end); if e then @@ -8018,19 +8333,19 @@ self:_handle_response_payload(e); end); end -function e:_handle_response(t,a,e) +function e:_handle_response(o,t,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 -if a==e then -self.bosh_waiting_requests[t]=nil; +for a,t in ipairs(self.bosh_waiting_requests)do +if t==e then +self.bosh_waiting_requests[a]=nil; break; end end else table.remove(self.bosh_waiting_requests,1); end -local e=self:_parse_response(t); +local e=self:_parse_response(o); if e then self:_handle_response_payload(e); end @@ -8040,7 +8355,7 @@ local e=t.tags; for t=1,#e do local e=e[t]; -if e.attr.xmlns==s then +if e.attr.xmlns==n then self:event("stream-"..e.name,e); elseif e.attr.xmlns then self:event("stream/"..e.attr.xmlns,e); @@ -8056,7 +8371,7 @@ stream_ns="http://jabber.org/protocol/httpbind",stream_tag="body", default_ns="jabber:client", streamopened=function(e,t)e.notopen=nil;e.payload=verse.stanza("body",t);return true;end; -handlestanza=function(t,e)t.payload:add_child(e);end; +handlestanza=function(e,t)e.payload:add_child(t);end; }; function e:_parse_response(e) self:debug("Parsing response: %s",e); @@ -8066,14 +8381,14 @@ return; end local t={notopen=true,stream=self}; -local a=r(t,a); +local a=h(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=n; +xmlns=s; content="text/xml; charset=utf-8"; sid=self.bosh_sid; rid=self.bosh_rid; @@ -8086,10 +8401,21 @@ end end) package.preload['verse.client']=(function(...) +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end local t=require"verse"; local o=t.stream_mt; -local h=require"util.jid".split; -local d=require"net.adns"; +local d=require"util.jid".split; +local h=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= @@ -8149,15 +8475,15 @@ end function o:connect_client(e,a) self.jid,self.password=e,a; -self.username,self.host,self.resource=h(e); +self.username,self.host,self.resource=d(e); self:add_plugin("tls"); self:add_plugin("sasl"); self:add_plugin("bind"); self:add_plugin("session"); function self.data(t,e) -local a,t=self.stream:feed(e); -if a then return;end -self:debug("Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," ")); +local t,a=self.stream:feed(e); +if t then return;end +self:debug("Received invalid XML (%s) %d bytes: %s",tostring(a),#e,e:sub(1,300):gsub("[\r\n]+"," ")); self:close("xml-not-well-formed"); end self:hook("connected",function()self:reopen();end); @@ -8218,22 +8544,22 @@ return self:close(e); end end -local function t() +local function a() self:connect(self.connect_host or self.host,self.connect_port or 5222); end if not(self.connect_host or self.connect_port)then -d.lookup(function(a) -if a then +h.lookup(function(t) +if t then local e={}; self.srv_hosts=e; -for a,t in ipairs(a)do +for a,t in ipairs(t)do table.insert(e,t.srv); end table.sort(e,s); -local a=e[1]; +local t=e[1]; self.srv_choice=1; -if a then -self.connect_host,self.connect_port=a.target,a.port; +if t then +self.connect_host,self.connect_port=t.target,t.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() @@ -8241,7 +8567,7 @@ self.srv_choice=self.srv_choice+1; local e=e[self.srv_choice]; self.connect_host,self.connect_port=e.target,e.port; -t(); +a(); return true; end end,1e3); @@ -8249,10 +8575,10 @@ self.srv_hosts=nil; end,1e3); end -t(); +a(); end,"_xmpp-client._tcp."..(self.host)..".","SRV"); else -t(); +a(); end end function o:reopen() @@ -8260,11 +8586,11 @@ 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 o:send_iq(e,a) -local t=self:new_id(); -self.tracked_iqs[t]=a; -e.attr.id=t; -self:send(e); +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 o:new_id() self.curr_id=self.curr_id+1; @@ -8272,15 +8598,26 @@ end end) package.preload['verse.component']=(function(...) -local a=require"verse"; -local o=a.stream_mt; -local d=require"util.jid".split; +local _ENV=_ENV; +local function e(t,...) +local e=package.loaded[t]or _ENV[t]or{_NAME=t}; +package.loaded[t]=e; +for t=1,select("#",...)do +(select(t,...))(e); +end +_ENV=e; +_M=e; +return e; +end +local t=require"verse"; +local a=t.stream_mt; +local h=require"util.jid".split; local e=require"lxp"; -local t=require"util.stanza"; -local r=require"util.sha1".sha1; -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 o=require"util.stanza"; +local d=require"util.hashes".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; +local r=require"util.xmppstream".new; local s="http://etherx.jabber.org/streams"; local i="jabber:component:accept"; local n={ @@ -8305,23 +8642,23 @@ end return t:event("stanza",e); end -function o:reset() +function a:reset() if self.stream then self.stream:reset(); else -self.stream=h(self,n); +self.stream=r(self,n); end self.notopen=true; return true; end -function o:connect_component(e,n) +function a:connect_component(e,n) self.jid,self.password=e,n; -self.username,self.host,self.resource=d(e); +self.username,self.host,self.resource=h(e); function self.data(t,e) -local t,a=self.stream:feed(e); +local t,o=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"); +a:debug("Received invalid XML (%s) %d bytes: %s",tostring(o),#e,e:sub(1,300):gsub("[\r\n]+"," ")); +a:close("xml-not-well-formed"); end self:hook("incoming-raw",function(e)return self.data(self.conn,e);end); self.curr_id=0; @@ -8335,30 +8672,30 @@ end end); self:hook("stanza",function(e) -local t; +local a; 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 -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")); +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")); return true; end else -t=self:event(e.name,e); -end -end -return t; +a=self:event(e.name,e); +end +end +return a; 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(t.stanza("handshake",{xmlns=i}):text(e)); +local e=d(self.stream_id..n,true); +self:send(o.stanza("handshake",{xmlns=i}):text(e)); self:hook("stream/"..i,function(e) if e.name=="handshake"then self:event("authentication-success"); @@ -8372,26 +8709,26 @@ self:connect(self.connect_host or self.host,self.connect_port or 5347); self:reopen(); end -function o:reopen() +function a:reopen() self:reset(); -self:send(t.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams', +self:send(o.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams', xmlns=i,version="1.0"}):top_tag()); end -function o:close(e) +function a:close(t) if not self.notopen then self:send(""); end -local t=self.conn.disconnect(); +local e=self.conn.disconnect(); self.conn:close(); -t(conn,e); -end -function o:send_iq(t,a) +e(conn,t); +end +function a:send_iq(t,a) local e=self:new_id(); self.tracked_iqs[e]=a; t.attr.id=e; self:send(t); end -function o:new_id() +function a:new_id() self.curr_id=self.curr_id+1; return tostring(self.curr_id); end @@ -8402,18 +8739,17 @@ local a=require"net.server"; local s=require"util.events"; local o=require"util.logger"; -local t={}; -local e=t; -t.server=a; +local e={}; +e.server=a; local t={}; t.__index=t; e.stream_mt=t; e.plugins={}; function e.init(...) for e=1,select("#",...)do -local a,t=pcall(require,"verse."..select(e,...)); -if not a then -error("Verse connection module not found: verse."..select(e,...)..t); +local t,a=pcall(require,"verse."..select(e,...)); +if not t then +error("Verse connection module not found: verse."..select(e,...)..a); end end return e; @@ -8433,9 +8769,9 @@ e.logger=o.init; e.new_logger=o.init; e.log=e.logger("verse"); -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)); +local function n(t,...) +local e,a,o=0,{...},select('#',...); +return(t:gsub("%%(.)",function(t)if e<=o then e=e+1;return tostring(a[e]);end end)); end function e.set_log_handler(e,t) t=t or{"debug","info","warn","error"}; @@ -8455,8 +8791,8 @@ end end end -function e._default_log_handler(o,a,t) -return io.stderr:write(o,"\t",a,"\t",t,"\n"); +function e._default_log_handler(a,t,o) +return io.stderr:write(a,"\t",t,"\t",o,"\n"); end e.set_log_handler(e._default_log_handler,{"error"}); local function o(t) @@ -8473,7 +8809,7 @@ return xpcall(a.step,o); end function e.quit() -return a.setquitting(true); +return a.setquitting("once"); end function t:listen(t,o) t=t or"localhost"; @@ -8485,18 +8821,18 @@ end return e,a; end -function t:connect(o,i) -o=o or"localhost"; -i=tonumber(i)or 5222; +function t:connect(i,o) +i=i or"localhost"; +o=tonumber(o)or 5222; local n=h.tcp() n:settimeout(0); n:setoption("keepalive",true); -local s,t=n:connect(o,i); +local s,t=n:connect(i,o); if not s and t~="timeout"then -self:warn("connect() to %s:%d failed: %s",o,i,t); +self:warn("connect() to %s:%d failed: %s",i,o,t); return self:event("disconnected",{reason=t})or false,t; end -local e=a.wrapclient(n,o,i,e.new_listener(self),"*a"); +local e=a.wrapclient(n,i,o,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; @@ -8541,8 +8877,8 @@ function t:hook(e,...) return self.events.add_handler(e,...); end -function t:unhook(t,e) -return self.events.remove_handler(t,e); +function t:unhook(e,t) +return self.events.remove_handler(e,t); end function e.eventable(e) e.events=s.new(); @@ -8582,10 +8918,10 @@ function a.onincoming(a,e) t:event("incoming-raw",e); end -function a.ondisconnect(e,a) -if e~=t.conn then return end +function a.ondisconnect(a,e) +if a~=t.conn then return end t.connected=false; -t:event("disconnected",{reason=a}); +t:event("disconnected",{reason=e}); end function a.ondrain(e) t:event("drained");