Thu, 23 Mar 2023 18:28:20 +0000
Add support for component connections
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"; a"encodings" idna={}; stringprep={}; base64={encode=e.b64,decode=e.unb64}; utf8={ valid=(utf8 and utf8.len)and function(e)return not not utf8.len(e);end or function()return true;end; }; return _M; end) package.preload['util.hashes']=(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 function t(t,e) error("Hash method "..e.." not available",2); end local e=setmetatable({},{__index=t}); local function t(t,o) local e,a; if t=="util.sha1"then e,a=true,require(t) else e,a=pcall(require,t); end if e then o(a);end end local function i(e) return(e:gsub(".",function(e) return("%02x"):format(e:byte()); end)); end t("util.sha1",function(t) e.sha1=t.sha1; 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(a,o,e)return t.pbkdf2(a,o,e,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); t("openssl.digest",function(o) local function a(t) return function(e,a) local e=o.new(t):final(e); if a then return i(e); end return e; end; end e.sha1=a("sha1"); end); return e; end) package.preload['lib.adhoc']=(function(...) 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.id".short; local e="http://jabber.org/protocol/commands"; local n={} local h={}; 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(e,a,i,t) return{name=e,node=a,handler=i,cmdtag=o,permission=(t or"user")}; end function h.handle_cmd(o,h,e) local t=e.tags[1].attr.sessionid or i(); local a={}; a.to=e.attr.to; a.from=e.attr.from; a.action=e.tags[1].attr.action or"execute"; a.form=e.tags[1]:child_with_ns("jabber:x:data"); local a,i=o:handler(a,n[t]); n[t]=i; local i=s.reply(e); local e; if a.status=="completed"then n[t]=nil; e=o:cmdtag("completed",t); elseif a.status=="canceled"then n[t]=nil; e=o:cmdtag("canceled",t); elseif a.status=="error"then n[t]=nil; i=s.error_reply(i,a.error.type,a.error.condition,a.error.message); h.send(i); return true; else e=o:cmdtag("executing",t); end for a,t in pairs(a)do if a=="info"then e:tag("note",{type="info"}):text(t):up(); elseif a=="warn"then e:tag("note",{type="warn"}):text(t):up(); elseif a=="error"then e:tag("note",{type="error"}):text(t.message):up(); elseif a=="actions"then local a=s.stanza("actions"); for i,t in ipairs(t)do if(t=="prev")or(t=="next")or(t=="complete")then a:tag(t):up(); else r:log("error",'Command "'..o.name.. '" at node "'..o.node..'" provided an invalid action "'..t..'"'); end end e:add_child(a); elseif a=="form"then e:add_child((t.layout or t):form(t.values)); elseif a=="result"then e:add_child((t.layout or t):form(t.values,"result")); elseif a=="other"then e:add_child(t); end end i:add_child(e); h.send(i); return true; end return h; end) package.preload['util.table']=(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{ pack=function(...)return{n=select("#",...);...}end; create=function()return{}end; move=function(o,t,i,a,e) e=e or o; if i>=t then local n=(i-t)+1; if a>i or a<=t or o~=e then for i=0,n-1 do e[a+i]=o[t+i]; end else for i=n-1,0,-1 do e[a+i]=o[t+i]; end end end return e; end; } end) package.preload['util.sha1']=(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 m=string.len local a=string.char local j=string.byte local k=string.sub local c=math.floor local t=require"util.bit" local q=t.bnot local e=t.band local y=t.bor local n=t.bxor local i=t.lshift local o=t.rshift local l,u,d,h,s local function p(t,e) return i(t,e)+o(t,32-e) end local function r(i) local t,o local t="" for n=1,8 do o=e(i,15) if(o<10)then t=a(o+48)..t else t=a(o+87)..t end i=c(i/16) end return t end local function b(t) local i,o local n="" i=m(t)*8 t=t..a(128) o=56-e(m(t),63) if(o<0)then o=o+64 end for e=1,o do t=t..a(0) end for t=1,8 do n=a(e(i,255))..n i=c(i/256) end return t..n end local function g(f) local m,t,o,a,w,r,c,v local i,i local i={} while(f~="")do for e=0,15 do i[e]=0 for t=1,4 do i[e]=i[e]*256+j(f,e*4+t) end end for e=16,79 do i[e]=p(n(n(i[e-3],i[e-8]),n(i[e-14],i[e-16])),1) end m=l t=u o=d a=h w=s for s=0,79 do if(s<20)then r=y(e(t,o),e(q(t),a)) c=1518500249 elseif(s<40)then r=n(n(t,o),a) c=1859775393 elseif(s<60)then r=y(y(e(t,o),e(t,a)),e(o,a)) c=2400959708 else r=n(n(t,o),a) c=3395469782 end v=p(m,5)+r+w+c+i[s] w=a a=o o=p(t,30) t=m m=v end l=e(l+m,4294967295) u=e(u+t,4294967295) d=e(d+o,4294967295) h=e(h+a,4294967295) s=e(s+w,4294967295) f=k(f,65) end end local function t(e,t) e=b(e) l=1732584193 u=4023233417 d=2562383102 h=271733878 s=3285377520 g(e) local e=r(l)..r(u)..r(d) ..r(h)..r(s); if t then return e; else return(e:gsub("..",function(e) return string.char(tonumber(e,16)); end)); end end return{ sha1=t; }; end) package.preload['util.bit']=(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=type; local h=tonumber; local n=setmetatable; local u=error; local l=tostring; local c={[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=0;[18]=3;[19]=2;[20]=5;[21]=4;[22]=7;[23]=6;[24]=9;[25]=8;[26]=11;[27]=10;[28]=13;[29]=12;[30]=15;[31]=14;[32]=2;[33]=3;[34]=0;[35]=1;[36]=6;[37]=7;[38]=4;[39]=5;[40]=10;[41]=11;[42]=8;[43]=9;[44]=14;[45]=15;[46]=12;[47]=13;[48]=3;[49]=2;[50]=1;[51]=0;[52]=7;[53]=6;[54]=5;[55]=4;[56]=11;[57]=10;[58]=9;[59]=8;[60]=15;[61]=14;[62]=13;[63]=12;[64]=4;[65]=5;[66]=6;[67]=7;[68]=0;[69]=1;[70]=2;[71]=3;[72]=12;[73]=13;[74]=14;[75]=15;[76]=8;[77]=9;[78]=10;[79]=11;[80]=5;[81]=4;[82]=7;[83]=6;[84]=1;[85]=0;[86]=3;[87]=2;[88]=13;[89]=12;[90]=15;[91]=14;[92]=9;[93]=8;[94]=11;[95]=10;[96]=6;[97]=7;[98]=4;[99]=5;[100]=2;[101]=3;[102]=0;[103]=1;[104]=14;[105]=15;[106]=12;[107]=13;[108]=10;[109]=11;[110]=8;[111]=9;[112]=7;[113]=6;[114]=5;[115]=4;[116]=3;[117]=2;[118]=1;[119]=0;[120]=15;[121]=14;[122]=13;[123]=12;[124]=11;[125]=10;[126]=9;[127]=8;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=0;[137]=1;[138]=2;[139]=3;[140]=4;[141]=5;[142]=6;[143]=7;[144]=9;[145]=8;[146]=11;[147]=10;[148]=13;[149]=12;[150]=15;[151]=14;[152]=1;[153]=0;[154]=3;[155]=2;[156]=5;[157]=4;[158]=7;[159]=6;[160]=10;[161]=11;[162]=8;[163]=9;[164]=14;[165]=15;[166]=12;[167]=13;[168]=2;[169]=3;[170]=0;[171]=1;[172]=6;[173]=7;[174]=4;[175]=5;[176]=11;[177]=10;[178]=9;[179]=8;[180]=15;[181]=14;[182]=13;[183]=12;[184]=3;[185]=2;[186]=1;[187]=0;[188]=7;[189]=6;[190]=5;[191]=4;[192]=12;[193]=13;[194]=14;[195]=15;[196]=8;[197]=9;[198]=10;[199]=11;[200]=4;[201]=5;[202]=6;[203]=7;[204]=0;[205]=1;[206]=2;[207]=3;[208]=13;[209]=12;[210]=15;[211]=14;[212]=9;[213]=8;[214]=11;[215]=10;[216]=5;[217]=4;[218]=7;[219]=6;[220]=1;[221]=0;[222]=3;[223]=2;[224]=14;[225]=15;[226]=12;[227]=13;[228]=10;[229]=11;[230]=8;[231]=9;[232]=6;[233]=7;[234]=4;[235]=5;[236]=2;[237]=3;[238]=0;[239]=1;[240]=15;[241]=14;[242]=13;[243]=12;[244]=11;[245]=10;[246]=9;[247]=8;[248]=7;[249]=6;[250]=5;[251]=4;[252]=3;[253]=2;[254]=1;[255]=0;}; local w={[0]=0;[1]=1;[2]=2;[3]=3;[4]=4;[5]=5;[6]=6;[7]=7;[8]=8;[9]=9;[10]=10;[11]=11;[12]=12;[13]=13;[14]=14;[15]=15;[16]=1;[17]=1;[18]=3;[19]=3;[20]=5;[21]=5;[22]=7;[23]=7;[24]=9;[25]=9;[26]=11;[27]=11;[28]=13;[29]=13;[30]=15;[31]=15;[32]=2;[33]=3;[34]=2;[35]=3;[36]=6;[37]=7;[38]=6;[39]=7;[40]=10;[41]=11;[42]=10;[43]=11;[44]=14;[45]=15;[46]=14;[47]=15;[48]=3;[49]=3;[50]=3;[51]=3;[52]=7;[53]=7;[54]=7;[55]=7;[56]=11;[57]=11;[58]=11;[59]=11;[60]=15;[61]=15;[62]=15;[63]=15;[64]=4;[65]=5;[66]=6;[67]=7;[68]=4;[69]=5;[70]=6;[71]=7;[72]=12;[73]=13;[74]=14;[75]=15;[76]=12;[77]=13;[78]=14;[79]=15;[80]=5;[81]=5;[82]=7;[83]=7;[84]=5;[85]=5;[86]=7;[87]=7;[88]=13;[89]=13;[90]=15;[91]=15;[92]=13;[93]=13;[94]=15;[95]=15;[96]=6;[97]=7;[98]=6;[99]=7;[100]=6;[101]=7;[102]=6;[103]=7;[104]=14;[105]=15;[106]=14;[107]=15;[108]=14;[109]=15;[110]=14;[111]=15;[112]=7;[113]=7;[114]=7;[115]=7;[116]=7;[117]=7;[118]=7;[119]=7;[120]=15;[121]=15;[122]=15;[123]=15;[124]=15;[125]=15;[126]=15;[127]=15;[128]=8;[129]=9;[130]=10;[131]=11;[132]=12;[133]=13;[134]=14;[135]=15;[136]=8;[137]=9;[138]=10;[139]=11;[140]=12;[141]=13;[142]=14;[143]=15;[144]=9;[145]=9;[146]=11;[147]=11;[148]=13;[149]=13;[150]=15;[151]=15;[152]=9;[153]=9;[154]=11;[155]=11;[156]=13;[157]=13;[158]=15;[159]=15;[160]=10;[161]=11;[162]=10;[163]=11;[164]=14;[165]=15;[166]=14;[167]=15;[168]=10;[169]=11;[170]=10;[171]=11;[172]=14;[173]=15;[174]=14;[175]=15;[176]=11;[177]=11;[178]=11;[179]=11;[180]=15;[181]=15;[182]=15;[183]=15;[184]=11;[185]=11;[186]=11;[187]=11;[188]=15;[189]=15;[190]=15;[191]=15;[192]=12;[193]=13;[194]=14;[195]=15;[196]=12;[197]=13;[198]=14;[199]=15;[200]=12;[201]=13;[202]=14;[203]=15;[204]=12;[205]=13;[206]=14;[207]=15;[208]=13;[209]=13;[210]=15;[211]=15;[212]=13;[213]=13;[214]=15;[215]=15;[216]=13;[217]=13;[218]=15;[219]=15;[220]=13;[221]=13;[222]=15;[223]=15;[224]=14;[225]=15;[226]=14;[227]=15;[228]=14;[229]=15;[230]=14;[231]=15;[232]=14;[233]=15;[234]=14;[235]=15;[236]=14;[237]=15;[238]=14;[239]=15;[240]=15;[241]=15;[242]=15;[243]=15;[244]=15;[245]=15;[246]=15;[247]=15;[248]=15;[249]=15;[250]=15;[251]=15;[252]=15;[253]=15;[254]=15;[255]=15;}; local p={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=0;[9]=0;[10]=0;[11]=0;[12]=0;[13]=0;[14]=0;[15]=0;[16]=0;[17]=1;[18]=0;[19]=1;[20]=0;[21]=1;[22]=0;[23]=1;[24]=0;[25]=1;[26]=0;[27]=1;[28]=0;[29]=1;[30]=0;[31]=1;[32]=0;[33]=0;[34]=2;[35]=2;[36]=0;[37]=0;[38]=2;[39]=2;[40]=0;[41]=0;[42]=2;[43]=2;[44]=0;[45]=0;[46]=2;[47]=2;[48]=0;[49]=1;[50]=2;[51]=3;[52]=0;[53]=1;[54]=2;[55]=3;[56]=0;[57]=1;[58]=2;[59]=3;[60]=0;[61]=1;[62]=2;[63]=3;[64]=0;[65]=0;[66]=0;[67]=0;[68]=4;[69]=4;[70]=4;[71]=4;[72]=0;[73]=0;[74]=0;[75]=0;[76]=4;[77]=4;[78]=4;[79]=4;[80]=0;[81]=1;[82]=0;[83]=1;[84]=4;[85]=5;[86]=4;[87]=5;[88]=0;[89]=1;[90]=0;[91]=1;[92]=4;[93]=5;[94]=4;[95]=5;[96]=0;[97]=0;[98]=2;[99]=2;[100]=4;[101]=4;[102]=6;[103]=6;[104]=0;[105]=0;[106]=2;[107]=2;[108]=4;[109]=4;[110]=6;[111]=6;[112]=0;[113]=1;[114]=2;[115]=3;[116]=4;[117]=5;[118]=6;[119]=7;[120]=0;[121]=1;[122]=2;[123]=3;[124]=4;[125]=5;[126]=6;[127]=7;[128]=0;[129]=0;[130]=0;[131]=0;[132]=0;[133]=0;[134]=0;[135]=0;[136]=8;[137]=8;[138]=8;[139]=8;[140]=8;[141]=8;[142]=8;[143]=8;[144]=0;[145]=1;[146]=0;[147]=1;[148]=0;[149]=1;[150]=0;[151]=1;[152]=8;[153]=9;[154]=8;[155]=9;[156]=8;[157]=9;[158]=8;[159]=9;[160]=0;[161]=0;[162]=2;[163]=2;[164]=0;[165]=0;[166]=2;[167]=2;[168]=8;[169]=8;[170]=10;[171]=10;[172]=8;[173]=8;[174]=10;[175]=10;[176]=0;[177]=1;[178]=2;[179]=3;[180]=0;[181]=1;[182]=2;[183]=3;[184]=8;[185]=9;[186]=10;[187]=11;[188]=8;[189]=9;[190]=10;[191]=11;[192]=0;[193]=0;[194]=0;[195]=0;[196]=4;[197]=4;[198]=4;[199]=4;[200]=8;[201]=8;[202]=8;[203]=8;[204]=12;[205]=12;[206]=12;[207]=12;[208]=0;[209]=1;[210]=0;[211]=1;[212]=4;[213]=5;[214]=4;[215]=5;[216]=8;[217]=9;[218]=8;[219]=9;[220]=12;[221]=13;[222]=12;[223]=13;[224]=0;[225]=0;[226]=2;[227]=2;[228]=4;[229]=4;[230]=6;[231]=6;[232]=8;[233]=8;[234]=10;[235]=10;[236]=12;[237]=12;[238]=14;[239]=14;[240]=0;[241]=1;[242]=2;[243]=3;[244]=4;[245]=5;[246]=6;[247]=7;[248]=8;[249]=9;[250]=10;[251]=11;[252]=12;[253]=13;[254]=14;[255]=15;} local y={[0]=15;[1]=14;[2]=13;[3]=12;[4]=11;[5]=10;[6]=9;[7]=8;[8]=7;[9]=6;[10]=5;[11]=4;[12]=3;[13]=2;[14]=1;[15]=0;}; local d={[0]=0;[1]=0;[2]=1;[3]=1;[4]=2;[5]=2;[6]=3;[7]=3;[8]=4;[9]=4;[10]=5;[11]=5;[12]=6;[13]=6;[14]=7;[15]=7;}; local r={[0]=0;[1]=8;[2]=0;[3]=8;[4]=0;[5]=8;[6]=0;[7]=8;[8]=0;[9]=8;[10]=0;[11]=8;[12]=0;[13]=8;[14]=0;[15]=8;}; local b={[0]=0;[1]=2;[2]=4;[3]=6;[4]=8;[5]=10;[6]=12;[7]=14;[8]=0;[9]=2;[10]=4;[11]=6;[12]=8;[13]=10;[14]=12;[15]=14;}; local v={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=1;[9]=1;[10]=1;[11]=1;[12]=1;[13]=1;[14]=1;[15]=1;}; local m={[0]=0;[1]=0;[2]=0;[3]=0;[4]=0;[5]=0;[6]=0;[7]=0;[8]=8;[9]=8;[10]=8;[11]=8;[12]=8;[13]=8;[14]=8;[15]=8;}; local i={__tostring=function(e)return("%x%x%x%x%x%x%x%x"):format(e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]);end}; local function s(a,t,e) return n({ e[a[1]*16+t[1]]; e[a[2]*16+t[2]]; e[a[3]*16+t[3]]; e[a[4]*16+t[4]]; e[a[5]*16+t[5]]; e[a[6]*16+t[6]]; e[a[7]*16+t[7]]; e[a[8]*16+t[8]]; },i); end local function a(e,t) return n({ t[e[1]]; t[e[2]]; t[e[3]]; t[e[4]]; t[e[5]]; t[e[6]]; t[e[7]]; t[e[8]]; },i); end local function f(t,e)return s(t,e,c);end local function c(e,t)return s(e,t,w);end local function w(e,t)return s(e,t,p);end local function p(e)return a(e,y);end local function s(t) local a=0; for e=1,8 do local o=d[t[e]]+a; a=r[t[e]]; t[e]=o; end end local function y(e,t) local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]}; for t=1,t do s(e);end return n(e,i); end local function s(e) local a=m[e[1]]; for t=1,8 do local o=d[e[t]]+a; a=r[e[t]]; e[t]=o; end end local function r(e,t) local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]}; for t=1,t do s(e);end return n(e,i); end local function s(t) local a=0; for e=8,1,-1 do local o=b[t[e]]+a; a=v[t[e]]; t[e]=o; end end local function d(e,t) local e={e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]}; for t=1,t do s(e);end return n(e,i); end local function a(e) if o(e)=="number"then e=("%x"):format(e); elseif o(e)=="table"then return e; elseif o(e)~="string"then u("string expected, got "..o(e),2);end local a={0,0,0,0,0,0,0,0}; e="00000000"..e; e=e:sub(-8); for t=1,8 do a[t]=h(e:sub(t,t),16)or u("Number format error",2); end return n(a,i); end local function t(t) return function(e,...) if o(e)~="table"then e=a(e);end e=t(e,...); e=h(l(e),16); if e>2147483647 then e=e-1-4294967295;end return e; end; end local function i(i) return function(e,t,...) if o(e)~="table"then e=a(e);end if o(t)~="table"then t=a(t);end e=i(e,t,...); e=h(l(e),16); if e>2147483647 then e=e-1-4294967295;end return e; end; end return{ bits=32; bxor=i(f); bor=i(c); band=i(w); bnot=t(p); lshift=t(d); rshift=t(y); arshift=t(r); cast=t(a); }; end) package.preload['util.stanza']=(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=error; local o=table.insert; local l=table.remove; local y=table.concat; local d=string.match; local p=tostring; local m=setmetatable; local q=getmetatable; local h=pairs; local s=ipairs; local t=type; local x=string.gsub; local u=string.sub; local r=string.find; local c=table.move or require"util.table".move; local w=require"util.table".create; local i=require"util.encodings".utf8.valid; local j,f=pcall(require,"util.termcolours"); local b="urn:ietf:params:xml:ns:xmpp-stanzas"; local v={xmlns=b}; local _ENV=nil; local e={__name="stanza"}; e.__index=e; local function n(e,t) return not r(e,t and"[^\1\9\10\13\20-\255]"or"[^\9\10\13\20-\255]"); end local function k(o,e) if t(o)~="string"then a("invalid "..e.." name: expected string, got "..t(o)); elseif#o==0 then a("invalid "..e.." name: empty string"); elseif r(o,"[<>& '\"]")then a("invalid "..e.." name: contains invalid characters"); elseif not n(o,e=="attribute")then a("invalid "..e.." name: contains control characters"); elseif not i(o)then a("invalid "..e.." name: contains invalid utf8"); end end local function g(e,o) if t(e)~="string"then a("invalid "..o.." value: expected string, got "..t(e)); elseif not n(e,false)then a("invalid "..o.." value: contains control characters"); elseif not i(e)then a("invalid "..o.." value: contains invalid utf8"); end end local function n(e) if e~=nil then if t(e)~="table"then a("invalid attributes: expected table, got "..t(e)); end for e,t in h(e)do k(e,"attribute"); g(t,"attribute"); end end end local function i(t,a,o) k(t,"tag"); n(a); local t={name=t,attr=a or{},namespaces=o,tags={}}; return m(t,e); end local function n(t) return q(t)==e; end function e:query(e) return self:tag("query",{xmlns=e}); end function e:body(t,e) return self:text_tag("body",t,e); end function e:text_tag(a,o,t,e) return self:tag(a,t,e):text(o):up(); end function e:tag(e,t,a) local t=i(e,t,a); local e=self.last_add; if not e then e={};self.last_add=e;end (e[#e]or self):add_direct_child(t); o(e,t); return self; end function e:text(e) if e~=nil and e~=""then local t=self.last_add; (t and t[#t]or self):add_direct_child(e); end return self; end function e:up() local e=self.last_add; if e then l(e);end return self; end function e:at_top() return self.last_add==nil or#self.last_add==0 end function e:reset() self.last_add=nil; return self; end function e:add_direct_child(e) if n(e)then o(self.tags,e); o(self,e); else g(e,"text"); o(self,e); end end function e:add_child(t) local e=self.last_add; (e and e[#e]or self):add_direct_child(t); return self; end function e:remove_children(a,t) t=t or self.attr.xmlns; return self:maptags(function(e) if(not a or e.name==a)and e.attr.xmlns==t then return nil; end return e; end); end function e:get_child(t,a) for o,e in s(self.tags)do if(not t or e.name==t) and((not a and self.attr.xmlns==e.attr.xmlns) or e.attr.xmlns==a)then return e; end end return nil; end function e:get_child_text(t,e) local e=self:get_child(t,e); if e then return e:get_text(); end return nil; end function e:get_child_attr(a,e,t) local e=self:get_child(a,e); if e then return e.attr[t]; end return nil; end function e:child_with_name(t) for a,e in s(self.tags)do if e.name==t then return e;end end return nil; end function e:child_with_ns(t) for a,e in s(self.tags)do if e.attr.xmlns==t then return e;end end return nil; end function e:get_child_with_attr(e,o,a,i,t) for e in self:childtags(e,o)do if(t and t(e.attr[a])or e.attr[a])==i then return e; end end return nil; end function e:children() local e=0; return function(t) e=e+1 return t[e]; end,self,e; end function e:childtags(t,a) local e=self.tags; local i,o=1,#e; return function() for o=i,o do local e=e[o]; if(not t or e.name==t) and((not a and self.attr.xmlns==e.attr.xmlns) or e.attr.xmlns==a)then i=o+1; return e; end end end; end function e:maptags(h) local i,t=self.tags,1; local n,o=#self,#i; local s=n+1; local e=1; while t<=o and o>0 do if self[e]==i[t]then local a=h(self[e]); if a==nil then l(self,e); l(i,t); n=n-1; o=o-1; e=e-1; t=t-1; else self[e]=a; i[t]=a; end t=t+1; end e=e+1; if e>s then a("Invalid stanza state! Please report this error."); end end return self; end function e:find(a) local e=1; local s=#a+1; repeat local o,t,i; local n=u(a,e,e); if n=="@"then return self.attr[u(a,e+1)]; elseif n=="{"then o,e=d(a,"^([^}]+)}()",e+1); end t,i,e=d(a,"^([^@/#]*)([/#]?)()",e); t=t~=""and t or nil; if e==s then if i=="#"then return self:get_child_text(t,o); end return self:get_child(t,o); end self=self:get_child(t,o); until not self end local function u(t,s) local n={}; for t,e in h(t.attr)do n[t]=e;end local a,i=t.namespaces; if a then i={}; for e,t in h(a)do i[e]=t;end end local o,a; if s then o={}; a={name=t.name,attr=n,namespaces=i,tags=o}; else o=w(#t.tags,0); a=w(#t,4); a.name=t.name; a.attr=n; a.namespaces=i; a.tags=o; end m(a,e); if not s then c(t,1,#t,1,a); c(t.tags,1,#t.tags,1,o); a:maptags(u); end return a; end local function l(e,o) if not n(e)then a("bad argument to clone: expected stanza, got "..t(e)); end return u(e,o); end local u={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; local function m(e)return(x(e,"['&<>\"]",u));end local function u(a,e,l,t,u) local i=0; local s=a.name o(e,"<"..s); for a,n in h(a.attr)do if r(a,"\1",1,true)then local s,a=d(a,"^([^\1]*)\1?(.*)$"); i=i+1; o(e," xmlns:ns"..i.."='"..t(s).."' ".."ns"..i..":"..a.."='"..t(n).."'"); elseif not(a=="xmlns"and n==u)then o(e," "..a.."='"..t(n).."'"); end end local i=#a; if i==0 then o(e,"/>"); else o(e,">"); for i=1,i do local i=a[i]; if i.name then l(i,e,l,t,a.attr.xmlns); else o(e,t(i)); end end o(e,"</"..s..">"); end end function e.__tostring(t) local e={}; u(t,e,u,m,nil); return y(e); end function e.top_tag(e) local e=l(e,true); return p(e):sub(1,-3)..">"; end function e.get_text(e) if#e.tags==0 then return y(e); end return nil; end function e.get_error(e) local i,t,a,o; local e=e:get_child("error"); if not e then return nil,nil,nil,nil; end i=e.attr.type; for i,e in s(e.tags)do if e.attr.xmlns==b then if not a and e.name=="text"then a=e:get_text(); elseif not t then t=e.name; end else o=e; end if t and a and o then break; end end return i,t or"undefined-condition",a,o; end function e.add_error(o,a,s,h,i) local e; if t(a)=="table"then if t(a.extra)=="table"then e=a.extra; end if t(a.context)=="table"and t(a.context.by)=="string"then i=a.context.by;end a,s,h=a.type,a.condition,a.text; end if o.attr.from==i then i=nil; end o:tag("error",{type=a,by=i}) :tag(s,v); if e and s=="gone"and t(e.uri)=="string"then o:text(e.uri); end o:up(); if h then o:text_tag("text",h,v);end if e and n(e.tag)then o:add_child(e.tag); elseif e and e.namespace and e.condition then o:tag(e.condition,{xmlns=e.namespace}):up(); end return o:up(); end local function u(i) local a={name=i.name,attr=i.attr}; for i,e in s(i)do if t(e)=="table"then o(a,u(e)); else o(a,e); end end return a; end e.__freeze=u; local function c(a) if a then local e=a.attr; local o={}; for e,a in h(e)do if t(e)=="string"then if r(e,"|",1,true)and not r(e,"\1",1,true)then local e,t=d(e,"^([^|]+)|(.+)$"); o[e.."\1"..t]=a; else o[e]=a; end end end local o=i(a.name,o); for a,e in s(a)do if t(e)=="table"then o:add_direct_child(c(e)); elseif t(e)=="string"then o:add_direct_child(e); end end return o; end end local function v(t,e) if not e then return i("message",t); else return i("message",t):text_tag("body",e); end end local function w(e) if not e then a("iq stanzas require id and type attributes"); end if not e.id then a("iq stanzas require an id attribute"); end if not e.type then a("iq stanzas require a type attribute"); end return i("iq",e); end local function h(e) if not n(e)then a("bad argument to reply: expected stanza, got "..t(e)); end return i(e.name, { to=e.attr.from, from=e.attr.to, id=e.attr.id, type=((e.name=="iq"and"result")or e.attr.type) }); end local function y(e,r,o,i,s) if not n(e)then a("bad argument to error_reply: expected stanza, got "..t(e)); elseif e.attr.type=="error"then a("bad argument to error_reply: got stanza of type error which must not be replied to"); end local e=h(e); e.attr.type="error"; e:add_error(r,o,i,s); e.last_add={e[1]}; return e; end local function d(e) return i("presence",e); end local s; if j then local a,t=f.getstyle,f.getstring; local h=a("1b3967"); local r=a("13b5ea"); local o=a("439639"); local n=a("a0ce67"); local i=a("d9541e"); local a=a("e96d1f"); local a=( t(n,"%1").. t(o,"%2").. t(i,"%3").. t(a,"%4").. t(i,"%5") ); local o=( t(o,"%1").. t(n,"%2").. t(o,"%3") ); function s(e) return(e:gsub("(<[?/]?)([^ >/?]*)(.-)([?/]?>)([^<]*)",function(e,i,s,n,d) return t(h,e)..t(r,i).. s:gsub("([^=]+)(=)([\"'])(.-)([\"'])",a).. t(h,n).. d:gsub("(&#?)(%w+)(;)",o); end,100)); end function e.pretty_print(e) return s(p(e)); end function e.pretty_top_tag(e) return s(e:top_tag()); end else e.pretty_print=e.__tostring; e.pretty_top_tag=e.top_tag; end function e.indent(i,e,a) if#i==0 or(#i==1 and t(i[1])=="string")then return i; end a=a or"\t"; e=e or 1; local o=l(i,true); for i in i:children()do if t(i)=="string"then if i:find("%S")then o:text("\n"..a:rep(e)); o:text(i); end elseif n(i)then o:text("\n"..a:rep(e)); o:add_direct_child(i:indent(e+1,a)); end end o:text("\n"..a:rep((e-1))); return o; end return{ stanza_mt=e; stanza=i; is_stanza=n; preserialize=u; deserialize=c; clone=l; message=v; iq=w; reply=h; error_reply=y; presence=d; xml_escape=m; pretty_print=s; }; end) package.preload['util.timer']=(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"util.indexedbheap"; local t=require"util.logger".init("timer"); local e=require"net.server"; local d=require"util.time".now local m=type; local i=debug.traceback; local r=tostring; local f=require"util.xpcall".xpcall; local c=math.max; local u=pairs; if e.timer then return e.timer; end local _ENV=nil; local h=e.add_task; local o; local s=0; local a=a.create(); local n={}; local e=nil; local function l(e)t("error","Traceback[timer]: %s",i(r(e),2));end local function r(o) local t; local i; while true do t=a:peek(); if t==nil or t>o then break;end local a,t,e=a:pop(); local s=n[e]; n[e]=nil; local h,a=f(t,l,o,e,s); if h and m(a)=="number"then if i then i[e]={t,a+o}; else i={[e]={t,a+o}}; end n[e]=s; end end if i then for o,e in u(i)do a:insert(e[1],e[2],o); end t=a:peek(); end if t~=nil and s>1 and t==e then t=nil; else e=t; end if t then return t-o; end s=s-1; end local function l(t,l,u) local i=d(); local t=i+t; local a=a:insert(l,t); n[a]=u; if e==nil or t<e then e=t; if o then o:close(); o=nil; else s=s+1; end o=h(e-i,r); end return a; end local function u(t) n[t]=nil; local s,n,i=a:remove(t); local t=a:peek(); if t~=e and o then e=t; o:close(); if e~=nil then o=h(c(e-d(),0),r); end end return s,n,i; end local function s(o,i) local n=d(); local t=n+i; a:reprioritize(o,i); if e==nil or t<e then e=t; h(e-n,r); end return o; end return{ add_task=l; stop=u; reschedule=s; }; end) package.preload['util.termcolours']=(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 r,h=table.concat,table.insert; local t,s=string.char,string.format; local o=tonumber; local c=ipairs; local m=io.write; local i=math.floor; local u=type; local d=setmetatable; local l=pairs; local a; if os.getenv("WINDIR")then a=require"util.windows"; end local n=a and a.get_consolecolor and a.get_consolecolor(); local _ENV=nil; local e={ reset=0;bright=1,dim=2,underscore=4,blink=5,reverse=7,hidden=8; black=30;red=31;green=32;yellow=33;blue=34;magenta=35;cyan=36;white=37; ["black background"]=40;["red background"]=41;["green background"]=42;["yellow background"]=43; ["blue background"]=44;["magenta background"]=45;["cyan background"]=46;["white background"]=47; bold=1,dark=2,underline=4,underlined=4,normal=0; } local y={ ["0"]=n, ["1"]=7+8, ["1;33"]=2+4+8, ["1;31"]=4+8 } local f={ [1]="font-weight: bold",[2]="opacity: 0.5",[4]="text-decoration: underline",[8]="visibility: hidden", [30]="color:black",[31]="color:red",[32]="color:green",[33]="color:#FFD700", [34]="color:blue",[35]="color: magenta",[36]="color:cyan",[37]="color: white", [40]="background-color:black",[41]="background-color:red",[42]="background-color:green", [43]="background-color:yellow",[44]="background-color:blue",[45]="background-color: magenta", [46]="background-color:cyan",[47]="background-color: white"; }; local w=t(27).."[%sm%s"..t(27).."[0m"; local function p(t,e) if t then return s(w,t,e); else return e; end end local function w(e) return i(e*3/32)+232; end local function v(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 t(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 d(e,{__index=function(a,e) if u(e)=="string"and e:find("%x%x%x%x%x%x")==1 then local a=e:sub(7)==" background"and"48;5;"or"38;5;"; return s("%s%d",a,v(t(e))); end end}); local t={ 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 l(t)do e[t]=e[t]or e[a]; t,a=t.." background",a.." background" e[t]=e[t]or e[a]; end local function s(...) local t,a={...},{}; for o,t in c(t)do t=e[t]; if t then h(a,t); end end return r(a,";"); end local t="0"; local function i(e) e=e or"0"; if e~=t then m("\27["..e.."m"); t=e; end end if a then function i(e) e=e or"0"; if e~=t then a.set_consolecolor(y[e]or n); t=e; end end if not n then function i()end end end local function a(t) if t=="0"then return"</span>";end local e={}; for t in t:gmatch("[^;]+")do h(e,f[o(t)]); end return"</span><span style='"..r(e,";").."'>"; end local function t(e) return e:gsub("\027%[(.-)m",a); end return{ getstring=p; getstyle=s; setstyle=i; tohtml=t; }; end) package.preload['util.uuid']=(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"util.random"; local a=t.bytes; local o=require"util.hex".encode; local i=math.ceil; local function e(e) return o(a(i(e/2))):sub(1,e); end local function o() return("%x"):format(a(1):byte()%4+8); end local function a() return e(8).."-"..e(4).."-4"..e(3).."-"..(o())..e(3).."-"..e(12); end return{ get_nibbles=e; generate=a; seed=t.seed; }; end) package.preload['util.time']=(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"socket".gettime; local e; local t,o=pcall(require,"system"); if t then e=o.monotime; end return{ now=a; monotonic=e; } end) package.preload['util.envload']=(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=load; local n=io.open; local function s(o,a,e) return t(o,a,nil,e); end local function i(a,i) local e,n,o=n(a); if not e then return e,n,o;end local a,t=t(e:lines(2048),"@"..a,nil,i); e:close(); return a,t; end return{envload=s,envloadfile=i}; end) package.preload['util.id']=(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=string.gsub; local t=require"util.random".bytes; local a=require"util.encodings".base64.encode; local i={["+"]="-",["/"]="_",["="]=""}; local function e(e) return(o(a(t(e)),"[+/=]",i)); end return{ tiny=function()return e(3);end; short=function()return e(9);end; medium=function()return e(18);end; long=function()return e(27);end; custom=function(t) return function()return e(t);end; end; } end) package.preload['util.serialization']=(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 u=getmetatable; local e,o=next,type; local n=string.format; local p=string.gsub; local y=string.rep; local a=string.char; local g=string.match; local H=table.concat; local S=require"util.hex".to; local N=pcall; local v=require"util.envload".envload; if not math.type then require"util.mathcompat" end local z,A=math.huge,-math.huge; local j=math.type; local function l(t) return e,t,nil; end local function h(t,e) error("Can't serialize "..o(t)..(e and": "..e or"")); end local function t(t,e) return n("{__type=%q,__error=%q}",o(t),e or"fail"); end local r={ ['\a']=[[\a]];['\b']=[[\b]]; ['\f']=[[\f]];['\n']=[[\n]]; ['\r']=[[\r]];['\t']=[[\t]]; ['\v']=[[\v]];['\\']=[[\\]]; ['\"']=[[\"]];['\'']=[[\']]; } for t=0,255 do local e=a(t); if not r[e]then r[e]=n("\\%03d",t); end end local a={ ["do"]=true;["and"]=true;["else"]=true;["break"]=true; ["if"]=true;["end"]=true;["goto"]=true;["false"]=true; ["in"]=true;["for"]=true;["then"]=true;["local"]=true; ["or"]=true;["nil"]=true;["true"]=true;["until"]=true; ["elseif"]=true;["function"]=true;["not"]=true; ["repeat"]=true;["return"]=true;["while"]=true; }; local function s(e) if o(e)~="table"then e={preset=e}; end local i={ table=true; string=true; number=true; boolean=true; ["nil"]=true; }; if e.preset=="debug"then e.preset="oneline"; e.freeze=true; e.fatal=false; e.fallback=t; e.unquoted=true; end if e.preset=="oneline"then e.indentwith=e.indentwith or""; e.itemstart=e.itemstart or" "; e.itemlast=e.itemlast or""; e.tend=e.tend or" }"; elseif e.preset=="compact"then e.indentwith=e.indentwith or""; e.itemstart=e.itemstart or""; e.itemlast=e.itemlast or""; e.equals=e.equals or"="; e.unquoted=true; end local s=e.fallback or e.fatal==false and t or h; local function h(e) return(i[o(e)]or s)(e); end local q=e.keywords or a; local f=e.indentwith or"\t"; local b=e.itemstart or"\n"; local v=e.itemsep or";"; local I=e.itemlast or";\n"; local w=e.tstart or"{"; local N=e.tend or"}"; local k=e.kstart or"["; local x=e.kend or"]"; local c=e.equals or" = "; local m=e.unquoted==true and"^[%a_][%w_]*$"or e.unquoted; local d=e.hex; local T=e.freeze; local E=e.maxdepth or 127; local _=e.multiref; local O=e.table_iterator or l; local function l(a,t,e,i) if t[a]then t[e],e=s(a,"table has multiple references"),e+1; return e; elseif i>E then t[e],e=s(a,"max table depth reached"),e+1; return e; end local d=a; t[a]=true; t[d]=true; if T==true then local i=u(a); if o(i)=="table"then local n=i.__name; local i=i.__freeze; if o(i)=="function"then a=i(a); if o(a)=="string"then t[e],e=a,e+1; return e; end if o(n)=="string"then t[e],e=n,e+1; end end end end t[e],e=w,e+1; local p=y(f,i); local r=1; local n,u; local w=false; for a,s in O(a)do w=true; t[e],e=b,e+1; t[e],e=p,e+1; n,u=o(a),o(s); if a==r then r=r+1; elseif m and n=="string"and not q[a]and g(a,m)then t[e],e=a,e+1; t[e],e=c,e+1; else t[e],e=k,e+1; if n=="table"then e=l(a,t,e,i+1); else t[e],e=h(a),e+1; end t[e],t[e+1],e=x,c,e+2; end if u=="table"then e=l(s,t,e,i+1); else t[e],e=h(s),e+1; end t[e],e=v,e+1; end if w then t[e-1]=I; t[e],e=y(f,i-1),e+1; end t[e],e=N,e+1; if _ then t[a]=nil; t[d]=nil; end return e; end function i.table(t) local e={}; l(t,e,1,1); return H(e); end local function a(e) return'"'..p(e,"[%z\1-\31\"\'\\\127-\255]",r)..'"'; end if o(d)=="string"then function i.string(e) local t=a(e); if#t>(#e*2+2+#d)then return d..'"'..S(e)..'"'; end return t; end else i.string=a; end function i.number(e) if j(e)=="integer"then return n("%d",e); elseif e==z then return"(1/0)"; elseif e==A then return"(-1/0)"; elseif e~=e then return"(0/0)"; end return n("%.18g",e); end i["nil"]=function() return"nil"; end function i.boolean(e) return e and"true"or"false"; end return h; end local function i(e) if o(e)~="string"then return nil;end e="return "..e; local e,t=v(e,"=serialized data",{}); if not e then return nil,t;end local t,e=N(e); if not t then return nil,e;end return e; end local a=s(); return{ new=s; serialize=function(e,t) if t==nil then return a(e); else return s(t)(e); end end; deserialize=i; }; end) package.preload['util.indexedbheap']=(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 d=setmetatable; local h=math.floor; local i=table.remove; local function l(t,i,o,n,s) local e=#t+1; while true do local a=h(e/2); if a==0 or i>t[a]then break;end t[e]=t[a]; o[e]=o[a]; s[o[e]]=e; e=a; end t[e]=i; o[e]=n; s[n]=e; end local function r(t,e,a,n) local i=t[e]; local s=a[e]; while e~=1 do local o=h(e/2); if i>=t[o]then break;end t[e]=t[o]; a[e]=a[o]; n[a[e]]=e; e=o; end t[e]=i; a[e]=s; n[s]=e; return e; end local function n(a,e,o,h) local s=a[e]; local n=o[e]; local i=#a; local t=2*e; while 2*e<=i do if t~=i and a[t]>a[t+1]then t=t+1; end if s>a[t]then a[e]=a[t]; o[e]=o[t]; h[o[e]]=e; else break; end e=t; t=2*e; end a[e]=s; o[e]=n; h[n]=e; return e; end local function u(e,t,a) local h=#e; if h==0 then return nil;end local s=e[1]; local o=t[1]; a[o]=nil; if h==1 then e[1]=nil; t[1]=nil; return s,o; end e[1]=i(e); t[1]=i(t); a[t[1]]=1; n(e,1,t,a); return s,o; end local t={}; function t:insert(a,t,e) if e==nil then e=self.current_id; self.current_id=e+1; end self.items[e]=a; l(self.priorities,t,self.ids,e,self.index); return e; end function t:pop() local a,e=u(self.priorities,self.ids,self.index); if e then local t=self.items[e]; self.items[e]=nil; return a,t,e; end end function t:peek() return self.priorities[1]; end function t:reprioritize(e,t) local e=self.index[e]; if e==nil then return;end self.priorities[e]=t; e=r(self.priorities,e,self.ids,self.index); n(self.priorities,e,self.ids,self.index); end function t:remove_index(e) local o=self.priorities[e]; if o==nil then return;end local t=self.ids[e]; local s=self.items[t]; local a=#self.priorities; self.priorities[e]=self.priorities[a]; self.ids[e]=self.ids[a]; self.index[self.ids[e]]=e; i(self.priorities); i(self.ids); self.index[t]=nil; self.items[t]=nil; if a>e then e=r(self.priorities,e,self.ids,self.index); n(self.priorities,e,self.ids,self.index); end return o,s,t; end function t:remove(e) return self:remove_index(self.index[e]); end local e={__index=t}; local e={ create=function() return d({ ids={}; items={}; priorities={}; index={}; current_id=1.5 },e); end }; return e; end) package.preload['util.xpcall']=(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=xpcall; if select(2,e(function(e)return e end,function()end,"test"))~="test"then e=require"util.compat".xpcall; end return{ xpcall=e; }; end) package.preload['util.array']=(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 d,l,u,m =table.insert,table.sort,table.remove,table.concat; local s=require"util.table".move; local h=setmetatable; local a=getmetatable; local r=math.random; local y=math.floor; local p,w=pairs,ipairs; local f=tostring; local c=type; local i={}; local t={}; local e={}; local o={ __index=e; __name="array"; __tostring=function(e)return"{"..e:concat(", ").."}";end; }; function o:__freeze()return self;end local function n(t,e,a,i) if c(e)=="function"then e=t.collect(e,a,i); end return h(e or{},o); end function o.__add(a,e) local t=n(); return t:append(a):append(e); end function o.__eq(e,t) if a(e)~=o or a(t)~=o then return false; end if#e==#t then for a=1,#e do if e[a]~=t[a]then return false; end end else return false; end return true; end function o.__div(a,i) local t=n(); local e=0; for o=1,#a do local a=i(a[o]); if a~=nil then e=e+1; t[e]=a; end end return t; end h(i,{__call=n}); function e:random() return self[r(1,#self)]; end function e:random_other(t) local e=#self; return self[((math.random(1,e-1)+(t-1))%e)+1]; end function t.map(e,t,o) for t,a in w(t)do e[t]=o(a); end return e; end function t.filter(t,a,n) local i,o=a==t,#a; local e=1; for o=1,o do local a=a[o]; if n(a)then t[e]=a; e=e+1; end end if i and e<=o then for e=e,o do t[e]=nil; end end return t; end function t.slice(o,t,a,e) if e==nil then e=-1; end if e<0 then e=#t+(e+1); end if a<0 then a=#t+(a+1); end if a<1 then a=1; end if e>#t then e=#t; end if a>e then for e=1,#o do o[e]=nil; end return o; end s(t,a,e,1,o); if t==o then s(t,#o+1,#o*2,2+e-a,t); end return o; end function t.sort(e,t,...) if t~=e then e:append(t); end l(e,...); return e; end function t.unique(o,e) local a={}; return t.filter(o,e,function(e) if a[e]then return false; else a[e]=true; return true; end end); end function t.pluck(t,e,i,o) for a=1,#e do local e=e[a][i]; if e==nil then e=o; end t[a]=e; end return t; end function t.reverse(e,i) local t=#i; if i==e then local a=y(t/2); t=t+1; local o; for a=1,a do o=t-a; e[a],e[o]=e[o],e[a]; end else local a=t+1; for t=1,t do e[t]=i[a-t]; end end return e; end function e:shuffle() local t=#self; for e=1,#self do local t=r(e,t); self[e],self[t]=self[t],self[e]; end return self; end function e:append(e) s(e,1,#e,#self+1,self); return self; end function e:push(t) d(self,t); return self; end e.pop=u; function e:concat(e) return m(i.map(self,f),e); end function e:length() return#self; end function i.collect(i,a,e) local t={}; while true do e=i(a,e); if e==nil then break;end d(t,e); end return h(t,o); end for t,a in p(t)do local a=a; i[t]=function(o,...) local t=n(); return a(t,o,...); end e[t]=function(e,...) return a(e,e,...); end end return i; end) package.preload['util.format']=(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=tostring; local p=table.unpack; local e=table.pack; local y=require"util.encodings".utf8.valid; local i=type; local w=require"util.serialization".new("debug"); local d=math.type; local u={c=true,d=true,i=true,o=true,u=true,X=true,x=true,}; local l={o=true;u=true;x=true;X=true}; local r={ ["\000"]="\226\144\128",["\001"]="\226\144\129",["\002"]="\226\144\130", ["\003"]="\226\144\131",["\004"]="\226\144\132",["\005"]="\226\144\133", ["\006"]="\226\144\134",["\007"]="\226\144\135",["\008"]="\226\144\136", ["\009"]="\226\144\137",["\010"]="\226\144\138",["\011"]="\226\144\139", ["\012"]="\226\144\140",["\013"]="\226\144\141",["\014"]="\226\144\142", ["\015"]="\226\144\143",["\016"]="\226\144\144",["\017"]="\226\144\145", ["\018"]="\226\144\146",["\019"]="\226\144\147",["\020"]="\226\144\148", ["\021"]="\226\144\149",["\022"]="\226\144\150",["\023"]="\226\144\151", ["\024"]="\226\144\152",["\025"]="\226\144\153",["\026"]="\226\144\154", ["\027"]="\226\144\155",["\028"]="\226\144\156",["\029"]="\226\144\157", ["\030"]="\226\144\158",["\031"]="\226\144\159",["\127"]="\226\144\161", }; local c=pcall(string.format,"%p",""); local function f(h,...) local o=e(...); local m=o.n; local a=0; h=h:gsub("%%[^cdiouxXaAeEfgGpqs%%]*[cdiouxXaAeEfgGpqs%%]",function(s) if s=="%%"then return end a=a+1; local e=o[a]; if e==nil then o[a]="nil"; return"(%s)"; end local t=s:sub(-1); local i=i(e); if t=="s"and i=="string"and not e:find("[%z\1-\31\128-\255]")then return elseif i=="number"then if t=="g"or(t=="d"and d(e)=="integer")then return end elseif t=="s"and i~="string"then e=n(e); i="string"; end if t~="s"and t~="q"and t~="p"then if i~="number"then e=n(e); t="s"; s="[%s]"; i="string"; elseif u[t]and d(e)~="integer"then o[a]=n(e); return"[%s]"; elseif l[t]and e<0 then o[a]=n(e); return"[%s]"; else return end end if t=="p"and not c then e=n(e); t="s"; s="[%s]"; i="string"; end if i=="string"and t~="p"then if not y(e)then t="q"; elseif t~="q"then o[a]=e:gsub("[%z\1-\8\11-\31\127]",r):gsub("\n\t?","\n\t"); return s; end end if t=="q"then o[a]=w(e); return"%s"; end if t=="p"and(i=="boolean"or i=="number")then o[a]=n(e); return"[%s]"; end end); while a<m do a=a+1; local e=o[a]; if e==nil then o[a]="(nil)"; else o[a]=n(e):gsub("[%z\1-\8\11-\31\127]",r):gsub("\n\t?","\n\t"); end h=h.." [%s]" end return h:format(p(o)); end return{ format=f; }; end) package.preload['util.promise']=(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={}; local n={__name="promise",__index=i}; local s=require"util.xpcall".xpcall; local l=table.unpack; function n:__tostring() return"promise ("..(self._state or"invalid")..")"; end local function o(e) local e=getmetatable(e); return e==n; end local function e(e,a,o,t) if not e then return t; end return function(t) local t,e=s(e,debug.traceback,t); if t then a(e); else o(e); end return true; end; end local function u(o,i,n,a,t) table.insert(o._pending_on_fulfilled,e(i,a,t,a)); table.insert(o._pending_on_rejected,e(n,a,t,t)); end local function d(i,o,n,t,a) e(o,t,a,t)(i.value); end local function r(i,n,o,a,t) e(o,a,t,t)(i.reason); end local function a(e,t,a,o,i) if e._state~="pending"then return; end e._state=t; e._next=a; for a,t in ipairs(o)do t(i); end e._pending_on_fulfilled=nil; e._pending_on_rejected=nil; return true; end local function h(e) local function i(t) if o(t)then t:next(h(e)); elseif a(e,"fulfilled",d,e._pending_on_fulfilled,t)then e.value=t; end end local function o(t) if a(e,"rejected",r,e._pending_on_rejected,t)then e.reason=t; end end return i,o; end local d=function(e) e(); end local function e(a) local e=setmetatable({_state="pending",_next=u,_pending_on_fulfilled={},_pending_on_rejected={}},n); if a then d(function() local o,t=h(e); local a,o=s(a,debug.traceback,o,t); if not a and e._state=="pending"then t(o); end end); end return e; end local function r(i) return e(function(n,r) local e,a,h=0,{},false; local t=0; for s,i in pairs(i)do if o(i)then t=t+1; i:next(function(o) a[s]=o; e=e+1; if e==t and h then n(a); end end,r); else a[s]=i; end end h=true; if e==t then n(a); end end); end local function u(n) return e(function(s) local e,t,i=0,{},false; local a=0; for n,h in pairs(n)do if o(h)then a=a+1; h:next(function(o) t[n]={status="fulfilled",value=o}; e=e+1; if e==a and i then s(t); end end,function(o) t[n]={status="rejected",reason=o}; e=e+1; if e==a and i then s(t); end end); else t[n]=h; end end i=true; if e==a then s(t); end end); end local function s(t,...) local e,a={...},select("#",...); return r(e):next(function(e) return t(l(e,1,a)); end); end local function n(t) return e(function(o,a) for e=1,#t do t[e]:next(o,a); end end); end local function a(t) return e(function(e) e(t); end); end local function t(t) return e(function(a,e) e(t); end); end local function h(e) return a():next(function()return e();end); end function i:next(t,a) return e(function(e,o) self:_next(t,a,e,o); end); end function i:catch(e) return self:next(nil,e); end function i:finally(e) local function i(t)e();return t;end local function o(a)e();return t(a);end return self:next(i,o); end return{ new=e; resolve=a; join=s; reject=t; all=r; all_settled=u; race=n; try=h; is_promise=o; set_nexttick=function(e)d=e;end; } end) package.preload['net.adns']=(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=setmetatable; local f=tostring; local y=table.concat; local n=string.format; local m=string.lower; local u=string.upper; local i=function()end; local w=require"util.logger"; local o=w.init("unbound"); local k=require"net.server"; local s=require"lunbound"; local g=require"util.promise"; local b=require"util.id".short; local p=require"socket".gettime; local e=require"util.dns"; local h,r,l=e.classes,e.types,e.errors; local v=e.parsers; local t={hoststxt=false} local function d(e) e=e or{}; for t,a in pairs(t)do if e[t]==nil then e[t]=a; end end for t,a in pairs(s.config)do if e[t]==nil then e[t]=a; end end return e; end local t; if prosody then local e=require"core.configmanager"; t=d(e.get("*","unbound")); prosody.events.add_handler("config-reloaded",function() t=d(e.get("*","unbound")); end); end local function q(e,t) o("debug","Setting up net.server event handling for %s",e); return t.watchfd(e,function() o("debug","Processing queries for %s",e); e:process() end); end local o,c; local function d() o=s.new(t); c=q(o,k); end if prosody then prosody.events.add_handler("server-started",d); end local s={ __tostring=function(e) if e._string then return e._string end local t=n("Status: %s",l[e.status]); if e.secure then t=t..", Secure"; elseif e.bogus then t=t..n(", Bogus: %s",e.bogus); end local a={t}; for t=1,#e do a[t+1]=e.qname.."\t"..h[e.qclass].."\t"..r[e.qtype].."\t"..f(e[t]); end local t=y(a,"\n"); e._string=t; return t; end; }; local n={}; local function y(e) if not e then return end local i=l[e.rcode]; local o=h[e.qclass]; local t=r[e.qtype]; e.status,e.class,e.type=i,o,t; local o=m(t); local i={__index=e,__tostring=function(e)return f(e[o])end}; local n=v[t]; for t=1,#e do if e.bogus then e[t]=nil; else e[t]=a({[o]=n(e[t])},i); end end return a(e,s); end local function m(c,l,a,t) if not o then d();end a=a and u(a)or"A"; t=t and u(t)or"IN"; local u,d=r[a],h[t]; local h=p(); local s; local i=w.init("unbound.query"..b()); local function r(e,o) local r=p(); n[s]=nil; if e then y(e); i("debug","Results for %s %s %s: %s (%s, %f sec)",l,t,a,e.rcode==0 and(#e.." items")or e.status, e.secure and"Secure"or e.bogus or"Insecure",r-h); else i("error","Results for %s %s %s: %s",l,t,a,f(o)); end local e,t=pcall(c,e,o); if not e then i("error","Error in callback: %s",t);end end i("debug","Resolve %s %s %s",l,t,a); local e; s,e=o:resolve_async(r,l,u,d); if s then n[s]=c; else i("error","Resolver error: %s",e); end return s,e; end local function s(a,e,t) if not o then d();end e=e and u(e)or"A"; t=t and u(t)or"IN"; local t,e=r[e],h[t]; local e,t=o:resolve(a,t,e); if not e then return e,t;end return y(e); end local function e(e) local t=n[e]; o:cancel(e); if t then t(nil,"canceled"); n[e]=nil; end return true; end local function a() for t in pairs(n)do e(t);end if c then c:close();end d(); return true; end local function t() error"not implemented"; end local a={ lookup=m; cancel=e; new_async_socket=t; dns={ lookup=s; cancel=e; cache=i; socket_wrapper_set=i; settimeout=i; query=i; purge=a; random=i; peek=i; types=r; classes=h; }; }; local function h(e,s,n,i) return g.new(function(o,t) local function a(a,e) if e then return t(e); else return o(a); end end local e,a=m(a,s,n,i) if not e then t(a);end end); end local e={ lookup=function(i,e,t,a,o) return m(e,t,a,o) end; lookup_promise=h; _resolver={ settimeout=function()end; closeall=function()end; }; } function a.resolver()return e;end return a; end) package.preload['util.dns']=(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=setmetatable; local e=table; local s=e.concat; local n=e.insert; local o=string.byte; local i=string.format; local h=string.sub; local c=require"util.dnsregistry"; local m=require"util.hex".encode; local r=require"util.net".ntop; local e={}; local function a(i,t) if o(i,t)==0 then return".",t+1;end local d,r,a=#i,{}; t=t or 1; repeat a=o(i,t)or 0; n(r,h(i,t+1,t+a)); t=t+a+1; until a==0 or t>=d; return s(r,"."),t; end e.CNAME=a; e.NS=a e.PTR=a; local p={ __tostring=function(e) return i("%s %s %d %d %d %d %d",e.mname,e.rname,e.serial,e.refresh,e.retry,e.expire,e.minimum); end; }; function e.SOA(n) local h,s,i; h,i=a(n,1); s,i=a(n,i); local m,f,w,y, v,g,b,j, x,q,k,c, u,i,n,a, o,r,l,d =o(n,i,i+19); return t({ mname=h; rname=s; serial=m*16777216+f*65536+w*256+y; refresh=v*16777216+g*65536+b*256+j; retry=x*16777216+q*65536+k*256+c; expire=u*16777216+i*65536+n*256+a; minimum=o*16777216+r*65536+l*256+d; },p); end e.A=r; e.AAAA=r; local r={ __tostring=function(e) return i("%d %s",e.pref,e.mx) end }; function e.MX(e) local i=a(e,3); local a,e=o(e,1,2); return t({ pref=a*256+e; mx=i; },r); end local r={ __tostring=function(e) return i("%d %d %d %s",e.priority,e.weight,e.port,e.target); end }; function e.SRV(e) local a=a(e,7); local e,o,h,n,i,s=o(e,1,6); return t({ priority=e*256+o; weight=h*256+n; port=i*256+s; target=a; },r); end local d={__tostring=s}; function e.TXT(s) local l=#s; local r,a,i={},1; repeat i=o(s,a)or 0; n(r,h(s,a+1,a+i)); a=a+i+1; until a>=l; return t(r,d); end e.SPF=e.TXT; local r={ [0]="PKIX-CA"; [1]="PKIX-EE"; [2]="DANE-TA"; [3]="DANE-EE"; [255]="PrivCert"; }; local d={ [0]="Cert", [1]="SPKI", [255]="PrivSel", }; local l={ [0]="Full", [1]="SHA2-256", [2]="SHA2-512", [255]="PrivMatch", }; local u={ __tostring=function(e) return i("%s %s %s %s", r[e.use]or e.use, d[e.select]or e.select, l[e.match]or e.match, m(e.data)); end; __index={ getUsage=function(e)return r[e.use]end; getSelector=function(e)return d[e.select]end; getMatchType=function(e)return l[e.match]end; } }; function e.TLSA(e) local i,o,a=o(e,1,3); return t({ use=i; select=o; match=a; data=h(e,4); },u); end local o={"alpn";"no-default-alpn";"port";"ipv4hint";"ech";"ipv6hint"}; t(o,{__index=function(t,e)return"key"..tostring(e);end}); local f={ __tostring=function(e) local t={}; for a=1,#e.fields do n(t,i("%s=%q",o[e.fields[a].key],tostring(e.fields[a].value))); end return i("%d %s %s",e.prio,e.name,s(t," ")); end; }; local u={__tostring=function(e)return s(e,", ");end} function e.SVCB(i) local o,s=i:byte(1,2); local l=o*256+s; local d,o=a(i,3); local r={}; while#i>o do local a,s=i:byte(o+0,o+1); local h,d=i:byte(o+2,o+3); local s=a*256+s; local h=h*256+d; local a=i:sub(o+4,o+4-1+h) if s==1 then a=t(e.TXT(a),u); elseif s==3 then local t,e=a:byte(1,2); local e=t*256+e; a=e; elseif s==4 then local o={}; for t=1,#a,4 do n(o,e.A(a:sub(t,t+3))); end a=t(o,u); elseif s==6 then local o={}; for t=1,#a,16 do n(o,e.AAAA(a:sub(t,t+15))); end a=t(o,u); end n(r,{key=s,value=a,len=h}); o=o+h+4; end return t({ prio=l,name=d,fields=r, },f); end e.HTTPS=e.SVCB; local o={ TLSA={ use=r; select=d; match=l; }; }; local a={ __tostring=function(e) return i([[\# %d %s]],#e.raw,m(e.raw)); end; }; local function i(e) return t({raw=e},a); end t(e,{__index=function()return i end}); return{ parsers=e; classes=c.classes; types=c.types; errors=c.errors; params=o; }; end) package.preload['net.server']=(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 w=function(e) return _G[e] end local W,e=require("util.logger").init("socket"),table.concat; local n=function(...)return W("debug",e{...});end local q=function(...)return W("warn",e{...});end local ie=1 local f=w"type" local j=w"pairs" local ne=w"ipairs" local p=w"tonumber" local u=w"tostring" local e=w"table" local a=w"string" local t=w"coroutine" local V=math.min local se=math.huge local ye=e.concat local he=e.insert local pe=a.sub local ve=t.wrap local be=t.yield local T=w"socket"or require"socket" local P=T.gettime local e=require"util.net"; local ke=e.pton; local ge=require"util.sslconfig"; local Y,je=pcall(require,"net.tls_luasec"); local me=T.bind local xe=T.select local H local Z local ce local qe local ee local d local we local te local ae local re local de local ue local oe local r local le local B local fe local v local i local F local h local s local R local b local y local g local z local a local o local k local M local C local U local E local D local X local l local L local S local I local O local N local _ local x local A v={} i={} h={} F={} s={} b={} y={} R={} g={} a=0 o=0 k=0 M=0 C=0 U=1 E=128 D=10 L=51e3*1024 S=25e3*1024 I=30 O=6e4 N=14*60 local e=package.config:sub(1,1)=="\\" x=(e and math.huge)or T._SETSIZE or 1024 _=T._SETSIZE or 1024 A=30 de=function(w,t,m,c,j,y,p) if t:getfd()>=x then q("server.lua: Disallowed FD number: "..t:getfd()) t:close() return nil,"fd-too-large" end local f=0 local b,e=w.onconnect,w.ondisconnect local k=t.accept local e={} e.shutdown=function()end e.ssl=function() return y~=nil end e.sslctx=function() return y end e.hosts={} e.remove=function() f=f-1 if e then e.resume() end end e.close=function() t:close() o=r(h,t,o) a=r(i,t,a) v[m..":"..c]=nil; s[t]=nil e=nil t=nil n"server.lua: closed server handler and removed sockets from list" end e.pause=function(o) if not e.paused then a=r(i,t,a) if o then s[t]=nil t:close() t=nil; end e.paused=true; n("server.lua: server [",m,"]:",c," paused") end end e.resume=function() if e.paused then if not t then t=me(m,c,E); t:settimeout(0) end a=d(i,t,a) s[t]=e g[e]=nil e.paused=false; n("server.lua: server [",m,"]:",c," resumed") end end e.ip=function() return m end e.serverport=function() return c end e.socket=function() return t end e.readbuffer=function() if a>=_ or o>=_ then e.pause() g[e]=l n("server.lua: refused new client connection: server full") return false end local t,i=k(t) if t then local a,o=t:getpeername() local t,i,e=B(e,w,t,a,c,o,j,y,p) if e then return false end f=f+1 n("server.lua: accepted new client connection from ",u(a),":",u(o)," to ",u(c)) if b and not p then return b(t); end return; elseif i then n("server.lua: error with new client connection: ",u(i)) e.pause() g[e]=l return false end end return e end B=function(k,c,t,N,te,I,D,p,Z,Q) if t:getfd()>=x then q("server.lua: Disallowed FD number: "..t:getfd()) t:close() if k then g[k]=l k.pause() end return nil,nil,"fd-too-large" end t:settimeout(0) local v local E local _ local J local j local K=c.onincoming local G=c.onstatus local x=c.ondisconnect local P=c.onpredrain local W=c.ondrain local ee=c.onreadtimeout; local U=c.ondetach local g={} local m=0 local X local F local f=0 local w=false local T=false local V,B=0,0 local O=L local S=S local e=g e.extra=Q if Q then e.servername=Q.servername end e.dispatch=function() return K end e.disconnect=function() return x end e.onreadtimeout=ee; e.setlistener=function(a,t,o) if U then U(a) end K=t.onincoming x=t.ondisconnect G=t.onstatus P=t.onpredrain W=t.ondrain e.onreadtimeout=t.onreadtimeout U=t.ondetach if t.onattach then t.onattach(a,o) end end e._setpending=function() j=true end e.getstats=function() return B,V end e.ssl=function() return J end e.sslctx=function() return p end e.ssl_info=function() return t.info and t:info() end e.ssl_peercertificate=function() if not t.getpeercertificate then return nil,"not-implemented";end return t:getpeercertificate() end e.ssl_peerverification=function() if not t.getpeerverification then return nil,{{"Chain verification not supported"}};end return t:getpeerverification(); end e.ssl_peerfinished=function() if not t.getpeerfinished then return nil,"not-implemented";end return t:getpeerfinished(); end e.send=function(n,i,o,a) return v(t,i,o,a) end e.receive=function(a,o) return E(t,a,o) end e.shutdown=function(a) return _(t,a) end e.setoption=function(i,a,o) if t.setoption then return t:setoption(a,o); end return false,"setoption not implemented"; end e.force_close=function(a,t) if m~=0 then n("server.lua: discarding unwritten data for ",u(N),":",u(I)) m=0; end return a:close(t); end e.close=function(l,d) if not e then return true;end a=r(i,t,a) b[e]=nil if m~=0 then e:sendbuffer() if m~=0 then if e then e.write=nil end X=true return false end end if t then z=_ and _(t) t:close() o=r(h,t,o) s[t]=nil t=nil else n"server.lua: socket already closed" end if e then y[e]=nil R[e]=nil local t=e; e=nil if x then x(t,d or false); x=nil end end if k then k.remove() end n"server.lua: closed client handler and removed socket from list" return true end e.server=function() return k end e.ip=function() return N end e.serverport=function() return te end e.clientport=function() return I end e.port=e.clientport local k=function(i,a) if not e then return false end f=f+#a if f>O then R[e]="send buffer exceeded" return false elseif not T and t and not h[t]then o=d(h,t,o) end m=m+1 g[m]=a if e then y[e]=y[e]or l end return true end e.write=k e.bufferqueue=function(t) return g end e.socket=function(a) return t end e.set_mode=function(a,t) D=t or D return D end e.set_send=function(a,t) v=t or v return v end e.bufferlen=function(o,t,a) O=a or O S=t or S return f,S,O end e.lock_read=function(t,a) q("server.lua, lock_read() is deprecated, use pause() and resume()") if a==true then return t:pause() elseif a==false then return t:resume() end return w end e.pause=function(o) local o=a a=r(i,t,a) b[e]=nil if a~=o then w=true end return w; end e.resume=function(o) if w then w=false a=d(i,t,a) b[e]=l end return w; end e.lock=function(t,a) q("server.lua, lock() is deprecated") e.lock_read(t,a) if a==true then e.pause_writes(t) elseif a==false then e.resume_writes(t) end return w,T end e.pause_writes=function(a) local a=o o=r(h,t,o) y[e]=nil T=true end e.resume_writes=function(e) T=false if f>0 and t then o=d(h,t,o) end end local b=function() local o,t,a=E(t,D) if not t or(t=="wantread"or t=="timeout")then local a=o or a or"" local o=#a if o>S then e:close("receive buffer exceeded") return false end local o=o*ie B=B+o C=C+o b[e]=l if j then j=nil if c.onconnect then c.onconnect(e) end end return K(e,a,t) else n("server.lua: client ",u(N),":",u(I)," read error: ",u(t)) z=e and e:force_close(t) return false end end local w=function() local w,a,i,s,d; if t then if j then j=nil if c.onconnect then c.onconnect(e); end end if P then P(e); end s=ye(g,"",1,m) w,a,i=v(t,s,1,f) d=(w or i or 0)*ie V=V+d M=M+d for e=m,1,-1 do g[e]=nil end else w,a,d=false,"unexpected close",0; end if w then m=0 f=0 o=r(h,t,o) y[e]=nil if W then W(e) end z=F and e:starttls(nil) z=X and e:force_close() return true elseif i and(a=="timeout"or a=="wantwrite")then s=pe(s,i+1,f) g[1]=s m=1 f=f-i y[e]=l return true else n("server.lua: client ",u(N),":",u(I)," write error: ",u(a)) z=e and e:force_close(a) return false end end local l; function e.set_sslctx(y,t) p=t; local u,f l=ve(function(s) local t for l=1,A do o=(f and r(h,s,o))or o a=(u and r(i,s,a))or a u,f=nil,nil l,t=s:dohandshake() if not t then n("server.lua: ssl handshake done") e.readbuffer=b e.sendbuffer=w l=G and G(e,"ssl-handshake-complete") if y.autostart_ssl and c.onconnect then c.onconnect(y); if m~=0 then o=d(h,s,o) end end a=d(i,s,a) return true else if t=="wantwrite"then o=d(h,s,o) f=true elseif t=="wantread"then a=d(i,s,a) u=true else break; end t=nil; be() end end t=(t or"handshake too long"); n("server.lua: ",t); z=e and e:force_close(t) return false,t end ) end if Y then e.starttls=function(f,c) if c then e:set_sslctx(c); end if m>0 then n"server.lua: we need to do tls, but delaying until send buffer empty" F=true return end n("server.lua: attempting to start tls on "..u(t)) local c,m=t t,m=p:wrap(t) if not t then n("server.lua: error while starting tls on client: ",u(m or"unknown error")) return nil,m end if t.sni then if f.servername then t:sni(f.servername); elseif next(p._sni_contexts)~=nil then t:sni(p._sni_contexts,true); end end t:settimeout(0) v=t.send E=t.receive _=H s[t]=e a=d(i,t,a) a=r(i,c,a) o=r(h,c,o) s[c]=nil e.starttls=nil F=nil J=true e.readbuffer=l e.sendbuffer=l return l(t) end end e.readbuffer=b e.sendbuffer=w v=t.send E=t.receive _=(J and H)or t.shutdown s[t]=e a=d(i,t,a) if p and Z and Y then n"server.lua: auto-starting ssl negotiation..." e.autostart_ssl=true; local e,t=e:starttls(p); if e==false then return nil,nil,t end end return e,t end H=function() end qe=function() return false end d=function(t,a,e) if not t[a]then e=e+1 t[e]=a t[a]=e end return e; end r=function(e,i,t) local o=e[i] if o then e[i]=nil local a=e[t] e[t]=nil if a~=i then e[a]=o e[o]=a end return t-1 end return t end oe=function(e) o=r(h,e,o) a=r(i,e,a) s[e]=nil e:close() end local function m(e,t,o) local a; local i=t.sendbuffer; function t.sendbuffer() i(t); if a and t.bufferlen()<o then e:lock_read(false); a=nil; end end local i=e.readbuffer; function e.readbuffer() i(); if not a and t.bufferlen()>=o then a=true; e:lock_read(true); end end e:set_mode("*a"); end te=function(t,e,l,h) t=t or"*" h=h or{} local o local r=h.tls_ctx; local c=h.tls_direct; local u=h.read_size; if f(l)~="table"then o="invalid listener table" elseif f(t)~="string"then o="invalid address" elseif f(e)~="number"or not(e>=0 and e<=65535)then o="invalid port" elseif v[t..":"..e]then o="listeners on '["..t.."]:"..e.."' already exist" elseif r and not Y then o="luasec not found" end if o then q("server.lua, [",t,"]:",e,": ",o) return nil,o end local o,h=me(t,e,E) if h then q("server.lua, [",t,"]:",e,": ",h) return nil,h end local h,l=de(l,o,t,e,u,r,c) if not h then o:close() return nil,l end o:settimeout(0) a=d(i,o,a) v[t..":"..e]=h s[o]=h n("server.lua: new "..(r and"ssl "or"").."server listener on '[",t,"]:",e,"'") return h end we=function(i,o,a,t,e) return te(i,o,a,{ read_size=t; tls_ctx=e; tls_direct=e and true or false; }); end re=function(t,e) return v[t..":"..e]; end le=function(e,t) local a=v[e..":"..t] if not a then return nil,"no server found on '["..e.."]:"..u(t).."'" end a:close() v[e..":"..t]=nil return true end ee=function() for e,t in j(s)do t:close() s[e]=nil end a=0 o=0 k=0 v={} i={} h={} F={} s={} end ue=function() return{ select_timeout=U; tcp_backlog=E; max_send_buffer_size=L; max_receive_buffer_size=S; select_idle_check_interval=I; send_timeout=O; read_timeout=N; max_connections=_; max_ssl_handshake_roundtrips=A; highest_allowed_fd=x; accept_retry_interval=D; } end fe=function(e) if f(e)~="table"then return nil,"invalid settings table" end U=p(e.select_timeout)or U L=p(e.max_send_buffer_size)or L S=p(e.max_receive_buffer_size)or S I=p(e.select_idle_check_interval)or I E=p(e.tcp_backlog)or E O=p(e.send_timeout)or O N=p(e.read_timeout)or N D=p(e.accept_retry_interval)or D _=e.max_connections or _ A=e.max_ssl_handshake_roundtrips or A x=e.highest_allowed_fd or x return true end ae=function(e) if f(e)~="function"then return nil,"invalid listener function" end k=k+1 F[k]=e return true end local u do local i={}; local t={}; function u(e,o) local a=P(); e=e+a; if e>=a then he(t,{e,o}); else local e=o(a); if e and f(e)=="number"then return u(e,o); end end end ae(function(a) if#t>0 then for a,e in j(t)do he(i,e); end t={}; end local e=se; for s,t in j(i)do local n,o=t[1],t[2]; if n<=a then i[s]=nil; local t=o(a); if f(t)=="number"then u(t,o); e=V(e,t); end else e=V(e,n-a); end end return e; end); end ce=function() return C,M,a,o,k end local t; local function c(e) t=e; end Z=function(e) if t then return"quitting";end if e then t="once";end l=P() repeat local e=se; for t=1,k do local t=F[t](l) if t then e=V(e,t);end end local e,a,o=xe(i,h,V(U,e)) for t,e in ne(e)do local t=s[e] if t then t:readbuffer() else oe(e) n"server.lua: found no handler and closed socket (readlist)" end end for e,t in ne(a)do local e=s[t] if e then e:sendbuffer() else oe(t) n"server.lua: found no handler and closed socket (writelist)" end end for e,t in j(R)do e.disconnect()(e,t) e:force_close() R[e]=nil; end l=P() if l-X>I then X=l for e,t in j(y)do if l-t>O then e.disconnect()(e,"send timeout") e:force_close() end end for e,t in j(b)do if l-t>N then if not(e.onreadtimeout)or e:onreadtimeout()~=true then e.disconnect()(e,"read timeout") e:close() else b[e]=l end end end end for e,t in j(g)do if l-t>D then g[e]=nil; e.resume(); end end until t; if t=="once"then t=nil;return;end ee(); return"quitting" end local function g() return Z(true); end local function k() return"select"; end local n=function(c,u,l,t,e,n,r) local e,t,r=B(nil,t,c,u,l,"clientport",e,n,n,r) if not e then return nil,r end s[t]=e if not n then e._setpending() a=d(i,t,a) o=d(h,t,o) end return e,t end local v=function(o,t,i,h,s,a,d) local e if f(i)~="table"then e="invalid listener table" elseif f(o)~="string"then e="invalid address" elseif f(t)~="number"or not(t>=0 and t<=65535)then e="invalid port" elseif s and not Y then e="luasec not found" end if not a then local e=ke(o); if not e then return nil,"invalid-ip";end if#e==16 then a="tcp6"; elseif#e==4 then a="tcp4"; end end local a=T[a]; if f(a)~="function"then e="invalid socket type" end if e then q("server.lua, addclient: ",e) return nil,e end local e,a=a() if a then return nil,a end e:settimeout(0) local r,a=e:setpeername(o,t) if r or a=="timeout"or a=="Operation already in progress"then return n(e,o,t,i,h,s,d) else return nil,a end end local p=function(e) local e=e.conn; o=r(h,e,o) a=r(i,e,a) s[e]=nil end; local a=function(n,t,l) local e=n.conn s[e]=n if t~=nil then if t then a=d(i,e,a) else o=r(h,e,o) end end if l~=nil then if l then o=d(h,e,o) else a=r(i,e,a) end end end local a=function(e,i,o) local t=e if f(e)=="number"then t={getfd=function()return e;end} end local e={ conn=t; readbuffer=i or H; sendbuffer=o or H; close=p; setflags=a; }; a(e,i,o) return e end w"setmetatable"(s,{__mode="k"}) w"setmetatable"(b,{__mode="k"}) w"setmetatable"(y,{__mode="k"}) X=P() local function o(e) local t=W; if e then W=e; end return t; end return{ _addtimer=ae, add_task=u; addclient=v, wrapclient=n, watchfd=a, loop=Z, link=m, step=g, stats=ce, closeall=ee, addserver=we, listen=te, getserver=re, setlogger=o, getsettings=ue, setquitting=c, removeserver=le, get_backend=k, changesettings=fe, tls_builder=function(e) return ge._new(je.new_context,e) end, } end) package.preload['util.xmppstream']=(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"lxp"; local x=require"util.stanza"; local k=x.stanza_mt; local w=error; local t=tostring; local d=table.insert; local g=table.concat; local z=table.remove; local b=setmetatable; local T=pcall(e.new,{StartDoctypeDecl=false}); local A=pcall(e.new,{XmlDecl=false}); local a=not not e.new({}).getcurrentbytecount; local _=1024*1024*1; local _ENV=nil; local j=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 h="http://etherx.jabber.org/streams"; local r="\1"; local q="^([^"..r.."]*)"..r.."?(.*)$"; local function o()end local function v(n,e,s) local i={}; local p=e.streamopened; local y=e.streamclosed; local u=e.error or function(o,a,e) w("XML stream error: "..t(a)..(e and": "..t(e)or""),2); end; local v=e.handlestanza; s=s or o; local t=e.stream_ns or h; local c=e.stream_tag or"stream"; if t~=""then c=t..r..c; end local x=t..r..(e.error_tag or"error"); local j=e.default_ns; local m="en"; local l={}; local h,e={}; local t=0; local r=0; function i:StartElement(f,o) if e and#h>0 then d(e,g(h)); h={}; end local h,i=f:match(q); if i==""then h,i="",h; end if h~=j or r>0 then o.xmlns=h; r=r+1; end for t=1,#o do local e=o[t]; o[t]=nil; local t=E[e]; if t then o[t]=o[e]; o[e]=nil; end end if not e then if a then t=self:getcurrentbytecount(); end if n.notopen then if f==c then r=0; m=o["xml:lang"]or m; if p then if a then s(t); t=0; end p(n,o); end else u(n,"no-stream",f); end return; end if h=="jabber:client"and i~="iq"and i~="presence"and i~="message"then u(n,"invalid-top-level-element"); end e=b({name=i,attr=o,tags={}},k); else if a then t=t+self:getcurrentbytecount(); end d(l,e); local t=e; e=b({name=i,attr=o,tags={}},k); d(t,e); d(t.tags,e); end end function i:StartCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else s(self:getcurrentbytecount()); end end end function i:EndCdataSection() if a then if e then t=t+self:getcurrentbytecount(); else s(self:getcurrentbytecount()); end end end function i:CharacterData(o) if e then if a then t=t+self:getcurrentbytecount(); end d(h,o); elseif a then s(self:getcurrentbytecount()); end end function i:EndElement(o) if a then t=t+self:getcurrentbytecount() end if r>0 then r=r-1; end if e then if#h>0 then d(e,g(h)); h={}; end if#l==0 then if a then s(t); end t=0; if e.attr["xml:lang"]==nil then e.attr["xml:lang"]=m; end if o~=x then v(n,e); else u(n,"stream-error",e); end e=nil; else e=z(l); end else if a then s(t); end if y then y(n); end end end local function o(e) u(n,"parse-error","restricted-xml","Restricted XML, see RFC 6120 section 11.1."); if not e.stop or not e:stop()then w("Failed to abort parsing"); end end if A then function i:XmlDecl(e,t,i) if a then s(self:getcurrentbytecount()); end if(t and t:lower()~="utf-8") or(i=="no") or(e and e~="1.0")then return o(self); end end end if T then i.StartDoctypeDecl=o; end i.Comment=o; i.ProcessingInstruction=o; local function a() e,h,t=nil,{},0; l={}; end local function t(t,e) n=e; end return i,{reset=a,set_session=t}; end local function u(l,d,o) local t=0; local e; if a then function e(e) t=t-e; end o=o or _; elseif o then w("Stanza size limits are not supported on this version of LuaExpat") end local n,s=v(l,d,e); local i=j(n,r,false); local h=i.parse; function l.open_stream(e,t,a) local i=e.sends2s or e.send; local o={ ["xmlns:stream"]="http://etherx.jabber.org/streams", ["xml:lang"]="en", xmlns=d.default_ns, version=e.version and(e.version>0 and"1.0"or nil), id=e.streamid or"", from=t or e.host,to=a, }; if e.stream_attrs then e:stream_attrs(t,a,o) end i("<?xml version='1.0'?>"..x.stanza("stream:stream",o):top_tag()); return true; end return{ reset=function() i=j(n,r,false); h=i.parse; t=0; s.reset(); end, feed=function(e,n) if a then t=t+#n; end local e=i; local n,s=h(e,n); if a and t>o then return nil,"stanza-too-large"; end if i~=e then e:parse(); e:close(); end return n,s; end, set_session=s.set_session; set_stanza_size_limit=function(t,e) o=e; end; }; end return{ ns_separator=r; ns_pattern=q; new_sax_handlers=v; new=u; }; end) package.preload['util.jid']=(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=select; local a,n=string.match,string.sub; local d=require"util.encodings".stringprep.nodeprep; local l=require"util.encodings".stringprep.nameprep; local u=require"util.encodings".stringprep.resourceprep; local i={ [" "]="\\20";['"']="\\22"; ["&"]="\\26";["'"]="\\27"; ["/"]="\\2f";[":"]="\\3a"; ["<"]="\\3c";[">"]="\\3e"; ["@"]="\\40";["\\"]="\\5c"; }; local s={}; local h={}; for t,e in pairs(i)do s[e]=t; h[e]=e:gsub("\\",i) end local _ENV=nil; local function t(e) if e==nil then return;end local i,t=a(e,"^([^@/]+)@()"); local t,o=a(e,"^([^@/]+)()",t); local a=t and a(e,"^/(.+)$",o); if(t==nil)or((a==nil)and#e>=o)then return nil,nil,nil;end return i,t,a; end local function m(e) local t,e=t(e); if t~=nil and e~=nil then return t.."@"..e; end return e; end local function r(e,o) local t,e,a=t(e); if e~=nil and e~="."then if n(e,-1,-1)=="."then e=n(e,1,-2); end e=l(e,o); if e==nil then return;end if t~=nil then t=d(t,o); if t==nil then return;end end if a~=nil then a=u(a,o); if a==nil then return;end end return t,e,a; end end local function n(t,e,a) if e==nil then return end if t~=nil and a~=nil then return t.."@"..e.."/"..a; elseif t~=nil then return t.."@"..e; elseif a~=nil then return e.."/"..a; end return e; end local function l(t,e) local a,t,e=r(t,e); return n(a,t,e); end local function d(e,a) local o,n,i=t(e); local e,t,a=t(a); if(e==nil or e==o)and (t==nil or t==n)and (a==nil or a==i)then return true end return false end local function c(e) return(o(1,t(e))); end local function a(e) return(o(2,t(e))); end local function u(e) return(o(3,t(e))); end local function o(e)return e and(e:gsub("\\%x%x",h):gsub("[\"&'/:<>@ ]",i));end local function i(e)return e and(e:gsub("\\%x%x",s));end return{ split=t; bare=m; prepped_split=r; join=n; prep=l; compare=d; node=c; host=a; resource=u; escape=o; unescape=i; }; end) package.preload['util.events']=(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=pairs; local l=table.insert; local m=table.remove; local r=table.sort; local d=setmetatable; local h=next; local _ENV=nil; local function f() local i={}; local t; local n={}; local o={}; local s=nil; local function u(n,i) local e=o[i]; if not e or h(e)==nil then return;end local t={}; for e in a(e)do l(t,e); end r(t,function(t,a)return e[t]>e[a];end); n[i]=t; return t; end; d(i,{__index=u}); local function d(t,n,a) local e=o[t]; if e then e[n]=a or 0; else e={[n]=a or 0}; o[t]=e; end i[t]=nil; end; local function r(t,a) local e=o[t]; if e then e[a]=nil; i[t]=nil; if h(e)==nil then o[t]=nil; end end end; local function f(e) return i[e]; end; local function w(e) for t,e in a(e)do d(t,e); end end; local function u(e) for t,e in a(e)do r(t,e); end end; local function h(t,a) local e=i[t]; if e and not s then for t=1,#e do local e=e[t](a); if e~=nil then return e;end end elseif e and s then for o=1,#e do local e=s(e[o],t,a); if e~=nil then return e;end end end end; local function c(s,r) local a=n[s]or t; if a then local e=#a; local function n(o,i) e=e-1; if e==0 then if t==nil or a==t then return h(o,i); end a,e=t,#t; return a[e](n,o,i); else return a[e](n,o,i); end end return a[e](n,s,r); end return h(s,r); end local function l(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,-1 do if e[t]==o then m(e,t); end end if#e==0 then if a==false then t=nil; else n[a]=nil; end end end local function e(e) local t=s; s=e; return t; end return{ add_handler=d; remove_handler=r; add_handlers=w; remove_handlers=u; get_handlers=f; wrappers={ add_handler=l; remove_handler=h; }; add_wrapper=l; remove_wrapper=h; set_debug_hook=e; fire_event=c; _handlers=i; _event_map=o; }; end return{ new=f; }; end) package.preload['util.dataforms']=(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=setmetatable; local n=ipairs; local s,p=type,next; local h=tonumber; local l=tostring; local d=table.concat; local f=require"util.stanza"; local u=require"util.jid".prep; local _ENV=nil; local y='jabber:x:data'; local m='http://jabber.org/protocol/xdata-validate'; local r={}; local a={__index=r}; local function c(t) return e(t,a); end local function w(e) local o={ title=e:get_child_text("title"); instructions=e:get_child_text("instructions"); }; for t in e:childtags("field")do local e={ 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]=e; if e.type then local a={}; if e.type:match"list%-"then for e in t:childtags("option")do a[#a+1]={label=e.attr.label,value=e:get_child_text("value")}; end for e in t:childtags("value")do a[#a+1]={label=e.attr.label,value=e:get_text(),default=true}; end elseif e.type:match"%-multi"then for e in t:childtags("value")do a[#a+1]=e.attr.label and{label=e.attr.label,value=e:get_text()}or e:get_text(); end if e.type=="text-multi"then e.value=d(a,"\n"); else e.value=a; end end end local t=t:get_child("validate",m); if t then e.datatype=datatype.attr.datatype; local t=t:get_child("range"); if t then e.range_min=h(t.attr.min); e.range_max=h(t.attr.max); end end end return c(o); end function r.form(t,h,i) if not i then i="form"end local e=f.stanza("x",{xmlns=y,type=i}); if i=="cancel"then return e; end if i~="submit"then if t.title then e:tag("title"):text(t.title):up(); end if t.instructions then e:tag("instructions"):text(t.instructions):up(); end end for t,a in n(t)do local o=a.type or"text-single"; e:tag("field",{type=o,var=a.var or a.name,label=i~="submit"and a.label or nil}); if i~="submit"then if a.desc then e:text_tag("desc",a.desc); end end if i=="form"and a.datatype then e:tag("validate",{xmlns=m,datatype=a.datatype}); if a.range_min or a.range_max then e:tag("range",{ min=a.range_min and l(a.range_min), max=a.range_max and l(a.range_max), }):up(); end e:up(); end local t=a.value; local r=a.options; if h and h[a.name]~=nil then t=h[a.name]; if i=="form"and s(t)=="table" and(o=="list-single"or o=="list-multi")then r,t=t,nil; end end if i=="form"and r then local a={}; for o,t in n(r)do if s(t)=="table"then e:tag("option",{label=t.label}):tag("value"):text(t.value):up():up(); if t.default then a[#a+1]=t.value; end else e:tag("option",{label=t}):tag("value"):text(t):up():up(); end end if not t then if o=="list-single"then t=a[1]; elseif o=="list-multi"then t=a; end end end if t~=nil then if s(t)=="number"then t=("%g"):format(t); end if o=="hidden"then if s(t)=="table"then e:tag("value") :add_child(t) :up(); else e:tag("value"):text(t):up(); end elseif o=="boolean"then e:tag("value"):text((t and"1")or"0"):up(); elseif o=="fixed"then e:tag("value"):text(t):up(); elseif o=="jid-multi"then for a,t in n(t)do e:tag("value"):text(t):up(); end elseif o=="jid-single"then e:tag("value"):text(t):up(); elseif o=="text-single"or o=="text-private"then e:tag("value"):text(t):up(); elseif o=="text-multi"then for t in t:gmatch("([^\r\n]+)\r?\n*")do e:tag("value"):text(t):up(); end elseif o=="list-single"then e:tag("value"):text(t):up(); elseif o=="list-multi"then for a,t in n(t)do e:tag("value"):text(t):up(); end end end local t=a.media; if t then e:tag("media",{xmlns="urn:xmpp:media-element",height=("%g"):format(t.height),width=("%g"):format(t.width)}); for a,t in n(t)do e:tag("uri",{type=t.type}):text(t.uri):up() end e:up(); end if i=="form"and a.required then e:tag("required"):up(); end e:up(); end return e; end local t={}; local l={}; function r.data(e,h,i) local o={}; local a={}; local s={}; for n,e in n(e)do local n; for t in h:childtags("field")do if(e.var or e.name)==t.attr.var then n=t; break; end end if not n then if i and i[e.name]~=nil then o[e.name]=i[e.name]; elseif e.required then a[e.name]="Required value missing"; end elseif e.name then s[e.name]=true; local t=t[e.type]; if t then local t,i=t(n,e.required); local n=e.datatype and l[e.datatype]; if t~=nil and n then local o,a=n(t,e); if o then t=a; else t,i=nil,a or("Invalid value for data of type "..e.datatype); end end o[e.name],a[e.name]=t,i; end end end if p(a)then return o,a,s; end return o,nil,s; end local function e(e,a) local e=e:get_child_text("value"); if a and(e==nil or e=="")then return nil,"Required value missing"; end return e; end t["text-single"]=e; t["text-private"]=e; t["jid-single"]= function(o,a) local e,a=e(o,a); if not e then return e,a;end local a=u(e); if not a then return nil,"Invalid JID: "..e; end return a; end t["jid-multi"]= function(o,i) local a={}; local e={}; for t in o:childtags("value")do local t=t:get_text(); local o=u(t); a[#a+1]=o; if t and not o then e[#e+1]=("Invalid JID: "..t); end end if#a>0 then return a,(#e>0 and d(e,"\n")or nil); elseif i then return nil,"Required value missing"; end end t["list-multi"]= function(o,a) local e={}; for t in o:childtags("value")do e[#e+1]=t:get_text(); end if#e>0 then return e; elseif a then return nil,"Required value missing"; end end t["text-multi"]= function(a,e) local e,a=t["list-multi"](a,e); if e then e=d(e,"\n"); end return e,a; end t["list-single"]=e; local o={ ["1"]=true,["true"]=true, ["0"]=false,["false"]=false, }; t["boolean"]= function(i,a) local e,a=e(i,a); if not e then return e,a;end local a=o[e]; if a==nil then return nil,"Invalid boolean representation:"..e; end return a; end t["hidden"]= function(e) return e:get_child_text("value"); end l["xs:integer"]= function(e,t) local e=h(e); if not e then return false,"not a number"; elseif e%1~=0 then return false,"not an integer"; end if t.range_max and e>t.range_max then return false,"out of bounds"; elseif t.range_min and e<t.range_min then return false,"out of bounds"; end return true,e; end local function t(e) if not f.is_stanza(e)then return nil,"not a stanza object"; elseif e.attr.xmlns~="jabber:x:data"or e.name~="x"then return nil,"not a dataform element"; end for e in e:childtags("field")do if e.attr.var=="FORM_TYPE"then return e:get_child_text("value"); end end return""; end return{ new=c; from_stanza=w; get_type=t; }; end) package.preload['util.caps']=(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 d=require"util.encodings".base64.encode; local u=require"util.hashes".sha1; local n,h,s=table.insert,table.sort,table.concat; local r=ipairs; local _ENV=nil; local function l(e) local o,a,i={},{},{}; for t,e in r(e)do if e.name=="identity"then n(o,(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(a,e.attr.var or""); elseif e.name=="x"and e.attr.xmlns=="jabber:x:data"then local t={}; local o; for a,e in r(e.tags)do if e.name=="field"and e.attr.var then local a={}; for t,e in r(e.tags)do e=#e.tags==0 and e:get_text(); if e then n(a,e);end end h(a); if e.attr.var=="FORM_TYPE"then o=a[1]; elseif#a>0 then n(t,e.attr.var.."\0"..s(a,"<")); else n(t,e.attr.var); end end end h(t); t=s(t,"<"); if o then t=o.."\0"..t;end n(i,t); end end h(o); h(a); h(i); if#o>0 then o=s(o,"<"):gsub("%z","/").."<";else o="";end if#a>0 then a=s(a,"<").."<";else a="";end if#i>0 then i=s(i,"<"):gsub("%z","<").."<";else i="";end local e=o..a..i; local t=d(u(e)); return t,e; end return{ calculate_hash=l; }; end) package.preload['util.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"util.stanza"; local a,u=table.insert,table.concat; local n=type; local e,s,c=next,pairs,ipairs; local r,l,d,h; local f="\n"; local o; local function e() error"Not implemented" end local function e() error"Not implemented" end local function y(e) return e:gsub("[,:;\\]","\\%1"):gsub("\n","\\n"); end local function p(e) return e:gsub("\\?[\\nt:;,]",{ ["\\\\"]="\\", ["\\n"]="\n", ["\\r"]="\r", ["\\t"]="\t", ["\\:"]=":", ["\\;"]=";", ["\\,"]=",", [":"]="\29", [";"]="\30", [","]="\31", }); end 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 n(t)=="table"then if t.types and e.TYPE then if n(e.TYPE)=="table"then for o,t in s(t.types)do for o,e in s(e.TYPE)do if e:upper()==t then a:tag(t):up(); break; end end end else a:tag(e.TYPE:upper()):up(); end end if t.props then for o,t in s(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,#e do a:tag(t.values[o]or i):text(e[o]):up(); end end end return a; end local function m(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 h(e) if not e[1]or e[1].name then return m(e) else local t=i.stanza("xCard",{xmlns="vcard-temp"}); for a=1,#e do t:add_child(m(e[a])); end return t; end end function r(t) t=t :gsub("\r\n","\n") :gsub("\n ","") :gsub("\n\n+","\n"); local s={}; local e; for t in t:gmatch("[^\n]+")do local t=p(t); local n,t,i=t:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); i=i:gsub("\29",":"); if#t>0 then local o={}; for a,i,n in t:gmatch("\30([^=]+)(=?)([^\30]*)")do a=a:upper(); local e={}; for t in n:gmatch("[^\31]+")do e[#e+1]=t e[t]=true; end if i=="="then o[a]=e; else o[a]=true; end end t=o; end if n=="BEGIN"and i=="VCARD"then e={}; s[#s+1]=e; elseif n=="END"and i=="VCARD"then e=nil; elseif e and o[n]then local o=o[n]; local n={name=n}; e[#e+1]=n; local s=e; e=n; if o.types then 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 e.TYPE=a; end end end if o.props then 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 c(t[a])do e[a]=t; end end end end end if o=="text"or o.value then a(e,i); elseif o.values then local t="\30"..i; for t in t:gmatch("\30([^\30]*)")do a(e,t); end end e=s; end end return s; end local function i(t) local e={}; for a=1,#t do e[a]=y(t[a]); end e=u(e,";"); local a=""; for e,t in s(t)do if n(e)=="string"and e~="name"then a=a..(";%s=%s"):format(e,n(t)=="table"and u(t,",")or t); 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,i(t[o])); end a(e,"END:VCARD") return u(e,f); end function l(e) if e[1]and e[1].name then return t(e) else local a={}; for o=1,#e do a[o]=t(e[o]); end return u(a,f); end end local function u(i) local t=i.name; local e=o[t]; local t={name=t}; if e=="text"then t[1]=i:get_text(); elseif n(e)=="table"then if e.value then 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,#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 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; end end if e.types then local e=e.types; t.TYPE={}; 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 t.TYPE=nil; end end if e.props then local e=e.props; 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); end end end else return nil end return t; end local function i(e) local t=e.tags; local e={}; for o=1,#t do a(e,u(t[o])); end return e end function d(e) if e.attr.xmlns~="vcard-temp"then return nil,"wrong-xmlns"; end if e.name=="xCard"then local a={}; local e=e.tags; for t=1,#e do a[t]=i(e[t]); end return a elseif e.name=="vCard"then return i(e) end end o={ VERSION="text", FN="text", N={ values={ "FAMILY", "GIVEN", "MIDDLE", "PREFIX", "SUFFIX", }, }, NICKNAME="text", PHOTO={ props_verbatim={ENCODING={"b"}}, props={"TYPE"}, value="BINVAL", }, BDAY="text", ADR={ types={ "HOME", "WORK", "POSTAL", "PARCEL", "DOM", "INTL", "PREF", }, values={ "POBOX", "EXTADD", "STREET", "LOCALITY", "REGION", "PCODE", "CTRY", } }, LABEL={ types={ "HOME", "WORK", "POSTAL", "PARCEL", "DOM", "INTL", "PREF", }, value="LINE", }, TEL={ types={ "HOME", "WORK", "VOICE", "FAX", "PAGER", "MSG", "CELL", "VIDEO", "BBS", "MODEM", "ISDN", "PCS", "PREF", }, value="NUMBER", }, EMAIL={ types={ "HOME", "WORK", "INTERNET", "PREF", "X400", }, value="USERID", }, JABBERID="text", MAILER="text", TZ="text", GEO={ values={ "LAT", "LON", }, }, TITLE="text", ROLE="text", LOGO="copy of PHOTO", AGENT="text", ORG={ values={ behaviour="repeat-last", "ORGNAME", "ORGUNIT", } }, CATEGORIES={ values="KEYWORD", }, NOTE="text", PRODID="text", REV="text", SORTSTRING="text", SOUND="copy of PHOTO", UID="text", URL="text", CLASS={ names={ "PUBLIC", "PRIVATE", "CONFIDENTIAL", }, }, KEY={ props={"TYPE"}, value="CRED", }, DESC="text", }; o.LOGO=o.PHOTO; o.SOUND=o.PHOTO; return{ from_text=r; to_text=l; from_xep54=d; to_xep54=h; lua_to_text=l; lua_to_xep54=h; text_to_lua=r; text_to_xep54=function(...)return h(r(...));end; xep54_to_lua=d; xep54_to_text=function(...)return l(d(...))end; }; end) package.preload['util.logger']=(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=pairs; local r=ipairs; local h=require; local s=table.remove; local _ENV=nil; local e={}; local t; local function n(e) local a=t(e,"debug"); local n=t(e,"info"); local i=t(e,"warn"); local o=t(e,"error"); return function(t,e,...) if t=="debug"then return a(e,...); elseif t=="info"then return n(e,...); elseif t=="warn"then return i(e,...); elseif t=="error"then return o(e,...); end end end function t(o,a) local t=e[a]; if not t then t={}; e[a]=t; end local e=function(i,...) for e=1,#t do t[e](o,a,i,...); end end return e; end local function d() for t,e in i(e)do for t=1,#e do e[t]=nil; end end end local function o(t,a) if not e[t]then e[t]={a}; else e[t][#e[t]+1]=a; end end local function l(i,s) local n=h"util.format".format; local function e(t,a,e,...) return i(t,a,n(e,...)); end for a,t in r(s or{"debug","info","warn","error"})do o(t,e); end return e; end local function h(o) local a; for t,e in i(e)do for t=#e,1,-1 do if e[t]==o then s(e,t); a=true; end end end return a; end return{ init=n; make_logger=t; reset=d; add_level_sink=o; add_simple_sink=l; new=t; remove_sink=h; }; end) package.preload['util.datetime']=(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=os.date; local i=os.time; local l=os.difftime; local t=math.floor; local s=tonumber; local _ENV=nil; local function d(a) return e("!%Y-%m-%d",a and t(a)or nil); end local function r(a) if a==nil or a%1==0 then return e("!%Y-%m-%dT%H:%M:%SZ",a); end local o=a%1; local a=t(a); return e("!%Y-%m-%dT%H:%M:%S.%%06dZ",a):format(t(o*1e6)); end local function u(a) if a==nil or a%1==0 then return e("!%H:%M:%S",a); end local o=a%1; local a=t(a); return e("!%H:%M:%S.%%06d",a):format(t(o*1e6)); end local function m(a) return e("!%Y%m%dT%H:%M:%S",a and t(a)or nil); end local function c(a) if a then local h,r,d,c,u,a,n=a:match("^(%d%d%d%d)%-?(%d%d)%-?(%d%d)T(%d%d):(%d%d):(%d%d%.?%d*)([Z+%-]?.*)$"); if h then local o=i(); local l=l(i(e("*t",o)),i(e("!*t",o))); local o=0; if n~=""and n~="Z"then local a,t,e=n:match("([+%-])(%d%d):?(%d*)"); if not a then return;end if#e~=2 then e="0";end t,e=s(t),s(e); o=t*60*60+e*60; if a=="-"then o=-o;end end local e=a%1; a=t(a+l)-o; return i({year=h,month=r,day=d,hour=c,min=u,sec=a,isdst=false})+e; end end end return{ date=d; datetime=r; time=u; legacy=m; parse=c; }; end) package.preload['util.json']=(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 u=type; local t,w,y=table.insert,table.concat,table.remove; local s=string.char; local x,d=tostring,tonumber; local p,l,z=pairs,ipairs,require"util.iterators".sorted_pairs; local i=next; local b,f=getmetatable,setmetatable; local r=print; local a,e=pcall(require,"util.array"); local v=a and b(e())or{}; local a={}; local n=f({},{__tostring=function()return"null";end;}); a.null=n; local m={ ["\""]="\\\"",["\\"]="\\\\",["\b"]="\\b", ["\f"]="\\f",["\n"]="\\n",["\r"]="\\r",["\t"]="\\t"}; for e=0,31 do local t=s(e); if not m[t]then m[t]=("\\u%.4X"):format(e);end end local function j(e) if e<128 then return s(e);end local t=e%64; if e<2048 then local e=(e-t)/64; return s(128+64+e,128+t); end local a=e%4096; local o=(a-t)/64; local e=(e-a)/4096; return s(128+64+32+e,128+o,128+t); end local k={ number=true, string=true, table=true, boolean=true }; local q={ __array=true; __hash=true; }; local o,g,h,c; function c(a,e) t(e,"\""..(a:gsub(".",m)).."\""); end function h(a,e) t(e,"["); if i(a)then for i,a in l(a)do o(a,e); t(e,","); end y(e); end t(e,"]"); end function g(d,e) local a={}; local s={}; local r={}; for t,e in l(d)do a[t]=e; end for e,t in p(d)do local o,i=u(e),u(t); if k[i]or t==n then if o=="string"and not q[e]then r[e]=t; elseif(k[o]or e==n)and a[e]==nil then s[e]=t; end end end if i(s)~=nil or i(r)~=nil or i(a)==nil then t(e,"{"); local n=#e; local d=e.ordered and z or p; for a,i in d(r)do c(a,e); t(e,":"); o(i,e); t(e,","); end if i(s)~=nil then t(e,"\"__hash\":["); for a,i in p(s)do o(a,e); t(e,","); o(i,e); t(e,","); end y(e); t(e,"]"); t(e,","); end if i(a)then t(e,"\"__array\":"); h(a,e); t(e,","); end if n~=#e then y(e);end t(e,"}"); else h(a,e); end end function o(e,a) local o=u(e); if e==n then t(a,"null"); elseif o=="number"then t(a,x(e)); elseif o=="string"then c(e,a); elseif o=="table"then local t=b(e); if t==v then h(e,a); else g(e,a); end elseif o=="boolean"then t(a,(e and"true"or"false")); else t(a,"null"); end end function a.encode(t) local e={}; o(t,e); return w(e); end function a.encode_ordered(t) local e={ordered=true}; o(t,e); return w(e); end function a.encode_array(t) local e={}; h(t,e); return w(e); end local function o(t,e) return t:find("[^ \t\r\n]",e)or e; end local function u(e) local a=e.__array; if a then e.__array=nil; for o,a in l(a)do t(e,a); end end local a=e.__hash; if a then e.__hash=nil; local t; for o,a in l(a)do if t~=nil then e[t]=a;t=nil; else t=a; end end end return e; end local i,h; local function c(t,e) local n={}; while true do local s,a; e=o(t,e+1); if t:byte(e)~=34 then if t:byte(e)==125 then return n,e+1;end return nil,"key expected"; end s,e=h(t,e); if s==nil then return nil,e;end e=o(t,e); if t:byte(e)~=58 then return nil,"colon expected";end a,e=i(t,e+1); if a==nil then return nil,e;end n[s]=a; e=o(t,e); local t=t:byte(e); if t==125 then return u(n),e+1;end if t~=44 then return nil,"object eof";end end end local function u(s,e) local n={}; while true do local a,h; a,e,h=i(s,e+1,93); if a==nil then if h then if#n~=0 then return nil,"value expected"; end a,e=f(n,v),e+1; end return a,e; end t(n,a); e=o(s,e); local t=s:byte(e); if t==93 then return f(n,v),e+1;end if t~=44 then return nil,"array eof";end end end local t; local function l(e) local t,e=d(e:sub(3,6),16),d(e:sub(9,12),16); local e=t*1024+e-56613888; local o=e%64; e=(e-o)/64; local a=e%64; e=(e-a)/64; local t=e%64; e=(e-t)/64; return s(240+e,128+t,128+a,128+o); end local function s(e) e=e:match("%x%x%x%x",3); if e then local e=d(e,16) if e>=55296 and e<=57343 then t=true;end return j(e); end t=true; end function h(o,e) e=e+1; local a=o:find("\"",e,true); if a then local e=o:sub(e,a-1); t=nil; e=e:gsub("\\u[dD][89abAB]%x%x\\u[dD][cdefCDEF]%x%x",l); 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 s(e,t) local e=e:match("[0-9%.%-eE%+]+",t); return d(e),t+#e; end local function d(t,e) local t,a,o=t:byte(e+1,e+3); if t==117 and a==108 and o==108 then return n,e+4; end return nil,"null parse failed"; end local function l(t,e) local o,t,a=t:byte(e+1,e+3); if o==114 and t==117 and a==101 then return true,e+4; end return nil,"true parse failed"; end local function n(t,e) local o,i,a,t=t:byte(e+1,e+4); if o==97 and i==108 and a==115 and t==101 then return false,e+5; end return nil,"false parse failed"; end function i(a,e,i) e=o(a,e); local t=a:byte(e); if t==123 then return c(a,e); elseif t==91 then return u(a,e); elseif t==34 then return h(a,e); elseif t~=nil and t>=48 and t<=57 or t==45 then return s(a,e); elseif t==110 then return d(a,e); elseif t==116 then return l(a,e); elseif t==102 then return n(a,e); elseif t==i then return nil,e,true; else return nil,"value expected"; end end local t={ ["\\\""]="\\u0022"; ["\\\\"]="\\u005c"; ["\\/"]="\\u002f"; ["\\b"]="\\u0008"; ["\\f"]="\\u000C"; ["\\n"]="\\u000A"; ["\\r"]="\\u000D"; ["\\t"]="\\u0009"; ["\\u"]="\\u"; }; function a.decode(e) e=e:gsub("\\.",t) local t,a=i(e,1); if t==nil then return t,a;end if e:find("[^ \t\r\n]",a)then return nil,"garbage at eof";end return t; end function a.test(e) local e=a.encode(e); local t=a.decode(e); local t=a.encode(t); if e~=t then r("FAILED"); r("encoded:",e); r("recoded:",t); else r(e); end return e==t; end return a; end) package.preload['util.xml']=(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"util.stanza"; local d=require"lxp"; local r=table.insert; local h=table.remove; local l=error; local _ENV=nil; local e=(function() local c={ ["http://www.w3.org/XML/1998/namespace"]="xml"; }; local n="\1"; local s="^([^"..n.."]*)"..n.."?(.*)$"; return function(u,o) local e={}; local a=t.stanza("root"); local t={}; local i={}; function e:StartNamespaceDecl(e,a) if e~=nil then r(t,a); r(i,e); end end function e:EndNamespaceDecl(e) if e~=nil then h(t); h(i); end end function e:StartElement(o,e) local o,n=o:match(s); if n==""then o,n="",o; end if o~=""then e.xmlns=o; end for t=1,#e do local a=e[t]; e[t]=nil; local t,o=a:match(s); if o~=""then t=c[t]; if t then e[t..":"..o]=e[a]; e[a]=nil; end end end local o={} for e=1,#t do o[i[e]]=t[e]; end a:tag(n,e,o); end function e:CharacterData(e) a:text(e); end function e:EndElement() a:up(); end local function t(t) if not t.stop or not t:stop()then l("Failed to abort parsing"); end end e.StartDoctypeDecl=t; if not o or not o.allow_comments then e.Comment=t; end if not o or not o.allow_processing_instructions then e.ProcessingInstruction=t; end local t=d.new(e,n); local e,o,n,i=t:parse(u); if e then e,o,n,i=t:parse();end if e then return a.tags[1]; else return e,("%s (line %d, col %d))"):format(o,n,i); end end; end)(); return{ parse=e; }; end) package.preload['util.rsm']=(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 h=require"util.stanza".stanza; local o,i=tostring,tonumber; local s=type; local r=pairs; local n='http://jabber.org/protocol/rsm'; local a={}; do local e=a; local function t(e) return i((e:get_text())); end local function a(t) return t:get_text(); end e.after=a; e.before=function(e) local e=e:get_text(); return e==""or e; end; e.max=t; e.index=t; e.first=function(e) return{index=i(e.attr.index);e:get_text()}; end; e.last=a; e.count=t; end local s=setmetatable({ first=function(t,e) if s(e)=="table"then t:tag("first",{index=e.index}):text(e[1]):up(); else t:tag("first"):text(o(e)):up(); end end; before=function(t,e) if e==true then t:tag("before"):up(); else t:tag("before"):text(o(e)):up(); end end },{ __index=function(t,e) return function(a,t) a:tag(e):text(o(t)):up(); end end; }); local function o(e) local o={}; for t in e:childtags()do local e=t.name; local a=e and a[e]; if a then o[e]=a(t); end end return o; end local function i(t) local e=h("set",{xmlns=n}); for t,o in r(t)do if a[t]then s[t](e,o); end end return e; end local function t(e) local e=e:get_child("set",n); if e and#e.tags>0 then return o(e); end end return{parse=o,generate=i,get=t}; 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{ seed=function()end; bytes=function(t)return e:read(t);end }; end local e=require"crypto" return e.rand; end) package.preload['util.ip']=(function(...) local _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 s=require"util.net"; local n=require"util.hex"; local a={}; local o={ __index=function(e,t) local a=a[t]; if not a then return nil;end local a=a(e); e[t]=a; return a; end, __tostring=function(e)return e.addr;end, }; o.__eq=function(e,t) if getmetatable(e)~=o or getmetatable(t)~=o then return false; end return e.packed==t.packed; end local h={ ["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 t(a,e) local i; if(not e or e=="IPv6")and a:find('%',1,true)then a,i=a:match("^(.-)%%(.*)"); end local t,n=s.pton(a); if not t then return t,n end if e=="IPv6"and#t~=16 then return nil,"invalid-ipv6"; elseif e=="IPv4"and#t~=4 then return nil,"invalid-ipv4"; elseif not e then if#t==16 then e="IPv6"; elseif#t==4 then e="IPv4"; else return nil,"unknown protocol"; end elseif e~="IPv6"and e~="IPv4"then return nil,"invalid protocol"; end return setmetatable({addr=a,packed=t,proto=e,zone=i},o); end function a:normal() return s.ntop(self.packed); end function a.bits(e) return n.encode(e.packed):upper():gsub(".",h); end function a.bits_full(e) if e.proto=="IPv4"then e=e.toV4mapped; end return e.bits; end local e; local function y(t,a) t,a=t.bits_full,a.bits_full; for e=1,128 do if t:sub(e,e)~=a:sub(e,e)then return e-1; end end return 128; end local i=t("::1"); local m=t("127.0.0.0"); local u=t("2002::"); local h=t("2001::"); local f=t("fe80::"); local w=t("169.254.0.0"); local c=t("fc00::"); local n=t("fec0::"); local r=t("3ffe::"); local d=t("::"); local v=t("ff00::"); local l=t("::ffff:0:0"); local function p(t) if e(t,m,8)then return 2; elseif e(t,w,16)then return 2; else return 14; end end local function w(t) if t==i then return 2; elseif e(t,f,10)then return 2; elseif e(t,n,10)then return 5; elseif e(t,v,10)then return t.packed:byte(2)%16; else return 14; end end local function m(t) if t==i then return 0; elseif e(t,u,16)then return 2; elseif e(t,h,32)then return 5; elseif e(t,c,7)then return 13; elseif e(t,n,10)then return 11; elseif e(t,r,16)then return 12; elseif e(t,d,96)then return 3; elseif e(t,l,96)then return 4; else return 1; end end local function f(t) if t==i then return 50; elseif e(t,u,16)then return 30; elseif e(t,h,32)then return 5; elseif e(t,c,7)then return 3; elseif e(t,n,10)then return 1; elseif e(t,r,16)then return 1; elseif e(t,d,96)then return 1; elseif e(t,l,96)then return 35; else return 40; end end function a:toV4mapped() if self.proto~="IPv4"then return nil,"No IPv4 address"end local e=t("::ffff:"..self.normal); return e; end function a:label() if self.proto=="IPv4"then return m(self.toV4mapped); else return m(self); end end function a:precedence() if self.proto=="IPv4"then return f(self.toV4mapped); else return f(self); end end function a:scope() if self.proto=="IPv4"then return p(self); else return w(self); end end local n=t("10.0.0.0"); local i=t("172.16.0.0"); local r=t("192.168.0.0"); local h=t("100.64.0.0"); function a:private() local t=self.scope~=14; if not t and self.proto=="IPv4"then return e(self,n,8)or e(self,i,12)or e(self,r,16)or e(self,h,10); end return t; end local function n(e) local o; local a=e:find("/",1,true); if a then o=tonumber(e:sub(a+1,-1)); e=e:sub(1,a-1); end return t(e),o; end function e(a,t,e) if not e or e>=128 or t.proto=="IPv4"and e>=32 then return a==t; elseif e<1 then return true; end if a.proto~=t.proto then if a.proto=="IPv4"then a=a.toV4mapped; elseif t.proto=="IPv4"then t=t.toV4mapped; e=e+(128-32); end end return a.bits:sub(1,e)==t.bits:sub(1,e); end local function i(e) return getmetatable(e)==o; end local function o(e,a) if a%8~=0 then return error("ip.truncate() only supports multiples of 8 bits"); end local a=a/8; if not i(e)then e=t(e); end return t(s.ntop(e.packed:sub(1,a)..("\0"):rep(#e.packed-a))) end return{ new_ip=t, commonPrefixLength=y, parse_cidr=n, match=e, is_ip=i; truncate=o; }; end) package.preload['util.hex']=(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=string.char; local s=string.format; local a=string.gsub; local h=string.lower; local o={}; local i={}; do local t,e; for a=0,255 do t,e=n(a),s("%02x",a); o[t]=e; i[e]=t; end end local function t(e) return(a(e,".",o)); end local function e(e) return(a(h(e),"%X*(%x%x)%X*",i)); end return{ encode=t,decode=e; to=t,from=e; }; end) package.preload['util.net']=(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{ pton=function(e) if e:find":"then return"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" else return"\0\0\0\0" end end } end) package.preload['util.sslconfig']=(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=type; local a=pairs; local t=rawset; local r=rawget; local f=error; local m=table.concat; local u=table.insert; local c=setmetatable; local l=require"util.paths".resolve_relative_path; local _ENV=nil; local n={}; local e={}; local d=function(e)return e end function n.options(h,s,e) local o=h[s]or{}; if i(e)~="table"then e={e}end for a,e in a(e)do if e==true or e==false then o[a]=e; else o[e]=true; end end t(h,s,o) end n.verifyext=n.options; function e.options(o) local t={}; for a,e in a(o)do if e then t[#t+1]=a; end end return t; end e.verifyext=e.options; function e.ciphers(t) if i(t)=="table"then return m(t,":"); end return t; end e.curveslist=e.ciphers; e.ciphersuites=e.ciphers; function e.key(t,a) if i(t)=="string"then return l(a._basedir,t); else return nil end end e.certificate=e.key; e.cafile=e.key; e.capath=e.key; e.dhparam=e.key; local o={"sslv2","sslv3","tlsv1","tlsv1_1","tlsv1_2","tlsv1_3"}; for e=1,#o do o[o[e].."+"]=e-1;end local function h(e) local t=o[e.protocol]; if t then e.protocol="sslv23"; for t=1,t do u(e.options,"no_"..o[t]); end end end local function s(o,e) t(o,"_cache",nil); if i(e)=="table"then for e,a in a(e)do if e:sub(1,1)~="_"then (n[e]or t)(o,e,a); end end end return o end local function i(i) local o={}; for t,a in a(i)do if t:sub(1,1)~="_"then o[t]=(e[t]or d)(a,i); end end h(o); return o; end local function n(e) local a=r(e,"_cache"); if a then return a,nil end local a,o=r(e,"_context_factory")(e:final(),e); if a then t(e,"_cache",a); end return a,o end local e={ __index={ apply=s; final=i; build=n; }; __newindex=function() f("SSL config objects cannot be modified directly. Use :apply()") end; }; local function n(t,a) return c({ _context_factory=t, _basedir=a, options={}, },e); end local function h(i) local o=n(); for e,a in a(i)do t(o,e,a); end return o end e.__index.clone=h; return{ apply=s; final=i; _new=n; }; end) package.preload['util.paths']=(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=table.concat; local e=package.config:sub(1,1); local a={} function a.resolve_relative_path(o,t) if t then o=o:gsub("%"..e.."+$",""); t=t:gsub("^%.%"..e.."+",""); local a; if e=="/"and t:sub(1,1)~="/"then a=true; elseif e=="\\"and(t:sub(1,1)~="/"and(t:sub(2,3)~=":\\"and t:sub(2,3)~=":/"))then a=true; end if a then return o..e..t; end end return t; end function a.glob_to_pattern(e) return"^"..e:gsub("[%p*?]",function(e) if e=="*"then return".*"; elseif e=="?"then return"."; else return"%"..e; end end).."$"; end function a.join(t,a,o,...) if a then if o then if...then return i({t,a,o,...},e); end return t..e..a..e..o; end return t..e..a; end return t; end function a.complement_lua_path(t) local o=_VERSION:match(" (.+)$"); local a=package.config:sub(3,3); local e=package.config:sub(1,1); local o=e.."lua"..e..o..e; if not string.find(package.path,t,1,true)then package.path=package.path..a..t..e.."share"..o.."?.lua"; package.path=package.path..a..t..e.."share"..o.."?"..e.."init.lua"; end if not string.find(package.path,t,1,true)then package.cpath=package.cpath..a..t..e.."lib"..o.."?.so"; end end return a; end) package.preload['util.mathcompat']=(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 if not math.type then local function t(e) if type(e)=="number"then if e%1==0 and e~=e+1 and e~=e-1 then return"integer" else return"float" end end end _G.math.type=t end end) package.preload['util.dnsregistry']=(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{ classes={ ["IN"]=1;[1]="IN"; ["CH"]=3;[3]="CH"; ["HS"]=4;[4]="HS"; ["ANY"]=255;[255]="ANY"; }; types={ ["A"]=1;[1]="A"; ["NS"]=2;[2]="NS"; ["MD"]=3;[3]="MD"; ["MF"]=4;[4]="MF"; ["CNAME"]=5;[5]="CNAME"; ["SOA"]=6;[6]="SOA"; ["MB"]=7;[7]="MB"; ["MG"]=8;[8]="MG"; ["MR"]=9;[9]="MR"; ["NULL"]=10;[10]="NULL"; ["WKS"]=11;[11]="WKS"; ["PTR"]=12;[12]="PTR"; ["HINFO"]=13;[13]="HINFO"; ["MINFO"]=14;[14]="MINFO"; ["MX"]=15;[15]="MX"; ["TXT"]=16;[16]="TXT"; ["RP"]=17;[17]="RP"; ["AFSDB"]=18;[18]="AFSDB"; ["X25"]=19;[19]="X25"; ["ISDN"]=20;[20]="ISDN"; ["RT"]=21;[21]="RT"; ["NSAP"]=22;[22]="NSAP"; ["NSAP-PTR"]=23;[23]="NSAP-PTR"; ["SIG"]=24;[24]="SIG"; ["KEY"]=25;[25]="KEY"; ["PX"]=26;[26]="PX"; ["GPOS"]=27;[27]="GPOS"; ["AAAA"]=28;[28]="AAAA"; ["LOC"]=29;[29]="LOC"; ["NXT"]=30;[30]="NXT"; ["EID"]=31;[31]="EID"; ["NIMLOC"]=32;[32]="NIMLOC"; ["SRV"]=33;[33]="SRV"; ["ATMA"]=34;[34]="ATMA"; ["NAPTR"]=35;[35]="NAPTR"; ["KX"]=36;[36]="KX"; ["CERT"]=37;[37]="CERT"; ["A6"]=38;[38]="A6"; ["DNAME"]=39;[39]="DNAME"; ["SINK"]=40;[40]="SINK"; ["OPT"]=41;[41]="OPT"; ["APL"]=42;[42]="APL"; ["DS"]=43;[43]="DS"; ["SSHFP"]=44;[44]="SSHFP"; ["IPSECKEY"]=45;[45]="IPSECKEY"; ["RRSIG"]=46;[46]="RRSIG"; ["NSEC"]=47;[47]="NSEC"; ["DNSKEY"]=48;[48]="DNSKEY"; ["DHCID"]=49;[49]="DHCID"; ["NSEC3"]=50;[50]="NSEC3"; ["NSEC3PARAM"]=51;[51]="NSEC3PARAM"; ["TLSA"]=52;[52]="TLSA"; ["SMIMEA"]=53;[53]="SMIMEA"; ["HIP"]=55;[55]="HIP"; ["NINFO"]=56;[56]="NINFO"; ["RKEY"]=57;[57]="RKEY"; ["TALINK"]=58;[58]="TALINK"; ["CDS"]=59;[59]="CDS"; ["CDNSKEY"]=60;[60]="CDNSKEY"; ["OPENPGPKEY"]=61;[61]="OPENPGPKEY"; ["CSYNC"]=62;[62]="CSYNC"; ["ZONEMD"]=63;[63]="ZONEMD"; ["SVCB"]=64;[64]="SVCB"; ["HTTPS"]=65;[65]="HTTPS"; ["SPF"]=99;[99]="SPF"; ["NID"]=104;[104]="NID"; ["L32"]=105;[105]="L32"; ["L64"]=106;[106]="L64"; ["LP"]=107;[107]="LP"; ["EUI48"]=108;[108]="EUI48"; ["EUI64"]=109;[109]="EUI64"; ["TKEY"]=249;[249]="TKEY"; ["TSIG"]=250;[250]="TSIG"; ["IXFR"]=251;[251]="IXFR"; ["AXFR"]=252;[252]="AXFR"; ["MAILB"]=253;[253]="MAILB"; ["MAILA"]=254;[254]="MAILA"; ["*"]=255;[255]="*"; ["URI"]=256;[256]="URI"; ["CAA"]=257;[257]="CAA"; ["AVC"]=258;[258]="AVC"; ["DOA"]=259;[259]="DOA"; ["AMTRELAY"]=260;[260]="AMTRELAY"; ["TA"]=32768;[32768]="TA"; ["DLV"]=32769;[32769]="DLV"; }; errors={ [0]="NoError";["NoError"]="No Error"; [1]="FormErr";["FormErr"]="Format Error"; [2]="ServFail";["ServFail"]="Server Failure"; [3]="NXDomain";["NXDomain"]="Non-Existent Domain"; [4]="NotImp";["NotImp"]="Not Implemented"; [5]="Refused";["Refused"]="Query Refused"; [6]="YXDomain";["YXDomain"]="Name Exists when it should not"; [7]="YXRRSet";["YXRRSet"]="RR Set Exists when it should not"; [8]="NXRRSet";["NXRRSet"]="RR Set that should exist does not"; [9]="NotAuth";["NotAuth"]="Server Not Authoritative for zone"; [10]="NotZone";["NotZone"]="Name not contained in zone"; [11]="DSOTYPENI";["DSOTYPENI"]="DSO-TYPE Not Implemented"; [16]="BADVERS";["BADVERS"]="Bad OPT Version"; [17]="BADKEY";["BADKEY"]="Key not recognized"; [18]="BADTIME";["BADTIME"]="Signature out of time window"; [19]="BADMODE";["BADMODE"]="Bad TKEY Mode"; [20]="BADNAME";["BADNAME"]="Duplicate key name"; [21]="BADALG";["BADALG"]="Algorithm not supported"; [22]="BADTRUNC";["BADTRUNC"]="Bad Truncation"; [23]="BADCOOKIE";["BADCOOKIE"]="Bad/missing Server Cookie"; }; }; end) package.preload['net.tls_luasec']=(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"ssl"; local o=t.newcontext; local s=t.context or require"ssl.context"; local n=io.open; local e={}; local h={__index=e}; function e:set_sni_host(a,t,e) local e,t=self._builder:clone():apply({ certificate=t, key=e, }):build(); if not e then return false,t end self._sni_contexts[a]=e._inner return true,nil end function e:remove_sni_host(e) self._sni_contexts[e]=nil end function e:wrap(e) local e,t,a=pcall(t.wrap,e,self._inner); if not e then return nil,a end return t,nil end local function i(e,i) if type(e.dhparam)=="string"then local t,a=n(e.dhparam); if not t then return nil,"Could not open DH parameters: "..a end local a=t:read("*a"); t:close(); e.dhparam=function()return a;end end local t,a=o(e); if not t then return nil,a end if t and e.ciphers then local o; o,a=s.setcipher(t,e.ciphers); if not o then return nil,a end end return setmetatable({ _inner=t, _builder=i, _sni_contexts={}, },h),nil end return{ new_context=i, }; end) package.preload['util.sasl.scram']=(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 s,h=require"mime".b64,require"mime".unb64; local t=require"util.hashes"; local i=require"bit"; local l=require"util.random"; local c=tonumber; local a,e=string.char,string.byte; local o=string.gsub; local i=i.bxor; local function r(n,t) return(o(n,"()(.)",function(n,o) return a(i(e(o),e(t,n))) end)); end local y,t=t.sha1,t.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 e=t(o,e); a=r(a,e); end return a; end local function f(e) return e; end local function a(e) return(o(e,"[,=]",{[","]="=2C",["="]="=3D"})); end local function n(e) if e:ssl()then local e=e:socket(); if e.info and e:info().protocol=="TLSv1.3"then if e.exportkeyingmaterial then return"p=tls-exporter",e:exportkeyingmaterial("EXPORTER-Channel-Binding",32,""); end elseif e.getfinished then return"p=tls-unique",e:getfinished(); end end end local function d(e,i) local a="n="..a(e.username); local o=s(l.bytes(15)); local u="r="..o; local l=a..","..u; local d=""; local a="n"; if i=="SCRAM-SHA-1-PLUS"then a,d=n(e.conn); elseif n(e.conn)then a="y"; end local n=a..",,"; local a=n..l; local a,m=coroutine.yield(a); if a~="challenge"then return false end local a,i,p=m:match("(r=[^,]+),s=([^,]*),i=(%d+)"); local c=c(p); i=h(i); if not a or not i or not c then return false,"Could not parse server_first_message"; elseif a:find(o,3,true)~=3 then return false,"nonce sent by server does not match our nonce"; elseif a==u then return false,"server did not append s-nonce to nonce"; end local o=n..d; local o="c="..s(o); local d=o..","..a; local a; local o; local n; if e.client_key and e.server_key then o=e.client_key; n=e.server_key; else if e.salted_password then a=e.salted_password; elseif e.password then a=w(f(e.password),i,c); end n=t(a,"Server Key"); o=t(a,"Client Key"); end local a=y(o); local e=l..","..m..","..d; local a=t(a,e); local o=r(o,a); local a=t(n,e); local e="p="..s(o); local e=d..","..e; local t,e=coroutine.yield(e); if t~="success"then return false,"success-expected"end local e=e:match("v=([^,]+)"); if h(e)~=a then return false,"server signature did not match"; end return true; end return function(e,t) if e.username and(e.password or(e.client_key or e.server_key))then if t=="SCRAM-SHA-1"then return d,99; elseif t=="SCRAM-SHA-1-PLUS"then if n(e.conn)then return d,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) return"success"==coroutine.yield("\0"..e.username.."\0"..e.password); end,5; end 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() return coroutine.yield()=="success"; end,0; end end end) package.preload['util.sasl.oauthbearer']=(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=="OAUTHBEARER"and t.username then return function(e) local t=e.bearer_token and("Bearer "..e.bearer_token)or""; local t,a=coroutine.yield("n,a="..e.username.."@"..e.host..",\001auth="..t.."\001"); if t=="success"then return true; elseif t=="challenge"then e:event("oauth-failure",{ json=a; }); if coroutine.yield("\001")~="failure"then error("Unexpected SASL state: expected failure after challenge"); end return false; end end,t.bearer_token and 6 or 4; end 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) local function i(o) if e.authenticated then return;end if o:get_child("starttls",t)and e.conn.starttls then e:debug("Negotiating TLS..."); e:send(a.stanza("starttls",{xmlns=t})); return true; elseif not e.conn.starttls and not e.secure then e:warn("SSL library (LuaSec) not loaded, so TLS not available"); elseif not e.secure then e:debug("Server doesn't offer TLS :("); end end local function o(t) if t.name=="proceed"then e:debug("Server says proceed, handshake starting..."); local t=a.tls_builder(".") :apply({mode="client",protocol="sslv23",options="no_sslv2",capath="/etc/ssl/certs"}) :apply(e.ssl or{}); e.conn:starttls(t:build(),true); end end local function a(t) if t=="ssl-handshake-complete"then e.secure=true; e:debug("Re-opening stream..."); e:reopen(); end end e:hook("stream-features",i,400); e:hook("stream/"..t,o); e:hook("status",a,400); return true; end end) package.preload['verse.plugins.sasl']=(function(...) local _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 s,r=require"mime".b64,require"mime".unb64; local o="urn:ietf:params:xml:ns:xmpp-sasl"; function n.plugins.sasl(e) local function d(t) if e.authenticated then return;end e:debug("Authenticating with SASL..."); local t=t:get_child("mechanisms",o); if not t then return end local a={}; local i={}; local h={}; for t in t:childtags("mechanism")do t=t:get_text(); e:debug("Server offers %s",t); h[t]=true; if not a[t]then local n=t:match("[^-]+"); local s,o=pcall(require,"util.sasl."..n:lower()); if s then e:debug("Loaded SASL %s module",n); a[t],i[t]=o(e,t); elseif not tostring(o):match("not found")then e:debug("Loading failed: %s",tostring(o)); end end end local t={}; for e in pairs(a)do table.insert(t,e); end if not t[1]then e:event("authentication-failure",{condition="no-supported-sasl-mechanisms",mechanisms=h}); e:close(); return; end table.sort(t,function(t,e)return i[t]>i[e];end); local t,i=t[1]; e:debug("Selecting %s mechanism...",t); e.sasl_mechanism=coroutine.wrap(a[t]); i=e:sasl_mechanism(t); local t=n.stanza("auth",{xmlns=o,mechanism=t}); if i then t:text(s(i)); end e:send(t); return true; end local function a(t) if t.name=="failure"then local a=t.tags[1]; local t=t:get_child_text("text"); e:event("authentication-failure",{condition=a.name,text=t}); e:close(); return false; end local t,a=e.sasl_mechanism(t.name,r(t:get_text())); if not t then e:event("authentication-failure",{condition=a}); e:close(); return false; elseif t==true then e:event("authentication-success"); e.authenticated=true e:reopen(); else e:send(n.stanza("response",{xmlns=o}):text(s(t))); end return true; end e:hook("stream-features",d,300); e:hook("stream/"..o,a); return true; 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 a="urn:ietf:params:xml:ns:xmpp-bind"; function t.plugins.bind(e) local function i(i) if e.bound then return;end e:debug("Binding resource..."); e:send_iq(t.iq({id="bind",type="set"}):tag("bind",{xmlns=a}):tag("resource"):text(e.resource), function(t) if t.attr.type=="result"then local t=t :get_child("bind",a) :get_child_text("jid"); e.username,e.host,e.resource=o.split(t); e.jid,e.bound=t,true; e:event("bind-success",{jid=t}); elseif t.attr.type=="error"then local a=t:child_with_name("error"); local o,t,a=t:get_error(); e:event("bind-failure",{error=t,text=a,type=o}); end end); end e:hook("stream-features",i,200); return true; end end) package.preload['verse.plugins.smacks']=(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 h=require"socket".gettime; local s="urn:xmpp:sm:3"; function n.plugins.smacks(e) local t=nil; local a=nil; local r=nil; local i; local o=nil; local function l(t) if o and(t.attr.xmlns=="jabber:client"or not t.attr.xmlns)then o=o+1; e:debug("Increasing handled stanzas to %d for %s",o,t:top_tag()); end end local function d(a) if t and(a.name and not a.attr.xmlns)then t[#t+1]=tostring(a); r=h(); if not i then i=true; e:debug("Waiting to send ack request..."); n.add_task(1,function() if#t==0 then i=false; return; end local a=h()-r; if a<1 and#t<10 then return 1-a; end e:debug("Time up, sending <r>..."); i=false; e:send(n.stanza("r",{xmlns=s})); end); end end end local function u() e:debug("smacks: connection lost"); e.stream_management_supported=nil; if e.resumption_token then e:debug("smacks: have resumption token, reconnecting in 1s..."); e.authenticated=nil; n.add_task(1,function() e:connect(e.connect_host or e.host,e.connect_port or 5222); end); return true; end end local function c() e.resumption_token=nil; end local function m(i) if i.name=="r"then e:debug("Ack requested... acking %d handled stanzas",o); e:send(n.stanza("a",{xmlns=s,h=tostring(o)})); elseif i.name=="a"then local o=tonumber(i.attr.h); if o>a then local i=#t; for a=a+1,o do table.remove(t,1); end e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")"); a=o; elseif o<a then e:warn("Received bad ack for "..o.." when last ack was "..a); end elseif i.name=="enabled"then o=0; e.pre_smacks_features=nil; if i.attr.id then e.resumption_token=i.attr.id; end elseif i.name=="resumed"then e.pre_smacks_features=nil; local o=tonumber(i.attr.h); if o>a then local i=#t; for a=a+1,o do table.remove(t,1); end e:debug("Received ack: New ack: "..o.." Last ack: "..a.." Unacked stanzas now: "..#t.." (was "..i..")"); a=o; end for a=1,#t do e:send(t[a]); end t={}; e:debug("Resumed successfully"); e:event("resumed"); elseif i.name=="failed"then e.bound=nil e.smacks=nil a=nil o=nil t={}; local t=e.pre_smacks_features; e.pre_smacks_features=nil; e:event("stream-features",t); else e:warn("Don't know how to handle "..s.."/"..i.name); end end local function i() if e.stream_management_supported and not e.smacks then e:debug("smacks: sending enable"); t={}; a=0; r=h(); e:send(n.stanza("enable",{xmlns=s,resume="true"})); e.smacks=true; end end local function a(t) if t:get_child("sm",s)then e.pre_smacks_features=t; e.stream_management_supported=true; if e.smacks and e.bound then e:debug("Resuming stream with %d handled stanzas",o); e:send(n.stanza("resume",{xmlns=s, h=tostring(o),previd=e.resumption_token})); return true; else end end end e:hook("stream-features",a,250); e:hook("stream/"..s,m); e:hook("bind-success",i,1); e:hook("stanza",l); e:hook("outgoing",d); e:hook("closed",c,100); e:hook("disconnected",u,100); 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; t.add_task(e.keepalive_timeout,function() e.conn:write(" "); return e.keepalive_timeout; end); 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 t=require"verse"; local e=require("mime").b64; local e=require("util.hashes").sha1; local r=require"util.caps".calculate_hash; local n="http://jabber.org/protocol/caps"; local e="http://jabber.org/protocol/disco"; local o=e.."#info"; local i=e.."#items"; function t.plugins.disco(e) e:add_plugin("presence"); local s={ __index=function(a,e) local t={identities={},features={}}; if e=="identities"or e=="features"then return a[false][e] end a[e]=t; return t; end, }; local a={ __index=function(t,a) local e={}; t[a]=e; return e; end, }; e.disco={ cache={}, info=setmetatable({ [false]={ identities={ {category='client',type='pc',name='Verse'}, }, features={ [n]=true, [o]=true, [i]=true, }, }, },s); items=setmetatable({[false]={}},a); }; e.caps={} e.caps.node='http://code.matthewwild.co.uk/verse/' local function h(a) local i=e.disco.info[a or false]; if a and a==e.caps.node.."#"..e.caps.hash then i=e.disco.info[false]; end local n,i=i.identities,i.features local e=t.stanza("query",{ xmlns=o, node=a, }); for a,t in pairs(n)do e:tag('identity',t):up() end for t in pairs(i)do e:tag('feature',{var=t}):up() end return e; end setmetatable(e.caps,{ __call=function(...) local a=r(h()) e.caps.hash=a; return t.stanza('c',{ xmlns=n, hash='sha-1', node=e.caps.node, ver=a }) end }) function e:set_identity(a,t) self.disco.info[t or false].identities={a}; e:event("disco-info-changed"); end function e:add_identity(a,t) local t=self.disco.info[t or false].identities; t[#t+1]=a; e:event("disco-info-changed"); end function e:add_disco_feature(t,a) local t=t.var or t; self.disco.info[a or false].features[t]=true; e:event("disco-info-changed"); end function e:remove_disco_feature(t,a) local t=t.var or t; self.disco.info[a or false].features[t]=nil; e:event("disco-info-changed"); end function e:add_disco_item(t,e) local e=self.disco.items[e or false]; e[#e+1]=t; end function e:remove_disco_item(a,e) local e=self.disco.items[e or false]; for t=#e,1,-1 do if e[t]==a then table.remove(e,t); end end end function e:jid_has_identity(e,a,t) local o=self.disco.cache[e]; if not o then return nil,"no-cache"; end local e=self.disco.cache[e].identities; if t then return e[a.."/"..t]or false; end for e in pairs(e)do if e:match("^(.*)/")==a then return true; end end end function e:jid_supports(e,t) local e=self.disco.cache[e]; if not e or not e.features then return nil,"no-cache"; end return e.features[t]or false; end function e:get_local_services(a,o) local e=self.disco.cache[self.host]; if not(e)or not(e.items)then return nil,"no-cache"; end local t={}; for i,e in ipairs(e.items)do if self:jid_has_identity(e.jid,a,o)then table.insert(t,e.jid); end end return t; end function e:disco_local_services(a) self:disco_items(self.host,nil,function(t) if not t then return a({}); end local e=0; local function o() e=e-1; if e==0 then return a(t); end end for a,t in ipairs(t)do if t.jid then e=e+1; self:disco_info(t.jid,nil,o); end end if e==0 then return a(t); end end); end function e:disco_info(e,a,h) local t=t.iq({to=e,type="get"}) :tag("query",{xmlns=o,node=a}); self:send_iq(t,function(t) if t.attr.type=="error"then return h(nil,t:get_error()); end local n,s,i={},{},{}; for e in t:get_child("query",o):childtags()do if e.name=="identity"then n[e.attr.category.."/"..e.attr.type]=e.attr.name or true; elseif e.name=="feature"then s[e.attr.var]=true; end end for t in t:get_child("query",o):childtags("x","jabber:x:data")do local e=t:get_child_with_attr("field",nil,"var","FORM_TYPE"); local e=e and e:get_child_text("value"); if e then i[e]=t; end end if not self.disco.cache[e]then self.disco.cache[e]={nodes={}}; end if a then if not self.disco.cache[e].nodes[a]then self.disco.cache[e].nodes[a]={nodes={}}; end self.disco.cache[e].nodes[a].identities=n; self.disco.cache[e].nodes[a].features=s; self.disco.cache[e].nodes[a].extended=i; else self.disco.cache[e].identities=n; self.disco.cache[e].features=s; self.disco.cache[e].extended=i; end return h(self.disco.cache[e]); end); end function e:disco_items(a,o,n) local t=t.iq({to=a,type="get"}) :tag("query",{xmlns=i,node=o}); self:send_iq(t,function(e) if e.attr.type=="error"then return n(nil,e:get_error()); end local t={}; for e in e:get_child("query",i):childtags()do if e.name=="item"then table.insert(t,{ name=e.attr.name; jid=e.attr.jid; node=e.attr.node; }); end end if not self.disco.cache[a]then self.disco.cache[a]={nodes={}}; end if o then if not self.disco.cache[a].nodes[o]then self.disco.cache[a].nodes[o]={nodes={}}; end self.disco.cache[a].nodes[o].items=t; else self.disco.cache[a].items=t; end return n(t); end); end e:hook("iq/"..o,function(a) local o=a.tags[1]; if a.attr.type=='get'and o.name=="query"then local o=h(o.attr.node); local t=t.reply(a):add_child(o); e:send(t); return true end end); e:hook("iq/"..i,function(o) local a=o.tags[1]; if o.attr.type=='get'and a.name=="query"then local n=e.disco.items[a.attr.node or false]; local t=t.reply(o):tag('query',{ xmlns=i, node=a.attr.node }) for a=1,#n do t:tag('item',n[a]):up() end e:send(t); return true end end); local t; e:hook("ready",function() if t then return;end t=true; local function a(a) local t=e.disco.cache[a]; if t then for t in pairs(t.identities)do local t,o=t:match("^(.*)/(.*)$"); e:event("disco/service-discovered/"..t,{ type=o,jid=a; }); end end end e:disco_info(e.host,nil,function() a(e.host); end); e:disco_local_services(function(t) for o,t in ipairs(t)do a(t.jid); end e:event("ready"); end); return true; end,50); e:hook("presence-out",function(t) t:remove_children("c",n); t:reset():add_child(e:caps()):reset(); end,10); e:hook("disco-info-changed",function() e:resend_presence(); end); end end) package.preload['verse.plugins.version']=(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 a="jabber:iq:version"; local function i(t,e) t.name=e.name; t.version=e.version; t.platform=e.platform; end function o.plugins.version(e) e.version={set=i}; e:hook("iq/"..a,function(t) if t.attr.type~="get"then return;end local t=o.reply(t) :tag("query",{xmlns=a}); if e.version.name then t:tag("name"):text(tostring(e.version.name)):up(); end if e.version.version then t:tag("version"):text(tostring(e.version.version)):up() end if e.version.platform then t:tag("os"):text(e.version.platform); end e:send(t); return true; end); function e:query_version(i,t) t=t or function(e)return self:event("version/response",e);end self:send_iq(o.iq({type="get",to=i}) :tag("query",{xmlns=a}), function(o) if o.attr.type=="result"then local e=o:get_child("query",a); local a=e and e:get_child_text("name"); local o=e and e:get_child_text("version"); local e=e and e:get_child_text("os"); t({ name=a; version=o; platform=e; }); else local e,a,o=o:get_error(); t({ error=true; condition=a; text=o; type=e; }); end end); end return true; end end) package.preload['verse.plugins.ping']=(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"socket".gettime; local s=require"util.id".short; local n="urn:xmpp:ping"; function t.plugins.ping(e) function e:ping(a,i) local h=o(); local s=s(); local t=t.iq{id=s,to=a,type="get"}:tag("ping",{xmlns=n}); e:send_iq(t, function(e) if e.attr.type=="error"then local o,e,t=e:get_error(); if e~="service-unavailable"and e~="feature-not-implemented"then i(nil,a,{type=o,condition=e,text=t}); return; end end i(o()-h,a); end); return s; end e:hook("iq/"..n,function(a) return e:send(t.reply(a)); end); return true; end end) package.preload['verse.plugins.uptime']=(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 t="jabber:iq:last"; local function a(e,t) e.starttime=t.starttime; end function o.plugins.uptime(e) e.uptime={set=a}; e:hook("iq/"..t,function(a) if a.attr.type~="get"then return;end local t=o.reply(a) :tag("query",{seconds=tostring(os.difftime(os.time(),e.uptime.starttime)),xmlns=t}); e:send(t); return true; end); function e:query_uptime(i,a) a=a or function(t)return e:event("uptime/response",t);end e:send_iq(o.iq({type="get",to=i}) :tag("query",{xmlns=t}), function(e) local t=e:get_child("query",t); if e.attr.type=="result"then local e=tonumber(t.attr.seconds); a({ seconds=e or nil; }); else local o,t,e=e:get_error(); a({ error=true; condition=t; text=e; type=o; }); end end); end return true; end end) package.preload['verse.plugins.time']=(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.datetime"; local o="urn:xmpp:time"; function e.plugins.time(t) function t:query_time(a,t) t=t or function(e)return self:event("time/response",e);end self:send_iq(e.iq({type="get",to=a}) :tag("time",{xmlns=o}), function(a) if a.attr.type=="result"then local e=a:get_child("time",o); local e={ tzo=e:get_child_text("tzo"); utc=e:get_child_text("utc"); }; if e.utc then e.timestamp=i.parse(e.utc); end t(e); else local e,a,o=a:get_error(); t({ error=true; condition=a; text=o; type=e; }); end end); end return true; end end) package.preload['verse.plugins.blocking']=(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 a="urn:xmpp:blocking"; function o.plugins.blocking(e) e.blocking={}; function e.blocking:block_jid(i,t) e:send_iq(o.iq{type="set"} :tag("block",{xmlns=a}) :tag("item",{jid=i}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:unblock_jid(i,t) e:send_iq(o.iq{type="set"} :tag("unblock",{xmlns=a}) :tag("item",{jid=i}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:unblock_all_jids(t) e:send_iq(o.iq{type="set"} :tag("unblock",{xmlns=a}) ,function()return t and t(true);end ,function()return t and t(false);end ); end function e.blocking:get_blocked_jids(t) e:send_iq(o.iq{type="get"} :tag("blocklist",{xmlns=a}) ,function(e) local a=e:get_child("blocklist",a); if not a then return t and t(false);end local e={}; for t in a:childtags()do e[#e+1]=t.attr.jid; end return t and t(e); end ,function(e)return t and t(false);end ); end end end) package.preload['verse.plugins.jingle']=(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 e=require"util.timer"; local o=require"util.id".short; local i="urn:xmpp:jingle:1"; local h="urn:xmpp:jingle:errors:1"; local t={}; t.__index=t; local e={}; local e={}; function a.plugins.jingle(e) e:hook("ready",function() e:add_disco_feature(i); end,10); function e:jingle(i) return a.eventable(setmetatable(base or{ role="initiator"; peer=i; sid=o(); stream=e; },t)); end function e:register_jingle_transport(e) end function e:register_jingle_content_type(e) end local function u(n) local s=n:get_child("jingle",i); local o=s.attr.sid; local r=s.attr.action; local o=e:event("jingle/"..o,n); if o==true then e:send(a.reply(n)); return true; end if r~="session-initiate"then local t=a.error_reply(n,"cancel","item-not-found") :tag("unknown-session",{xmlns=h}):up(); e:send(t); return; end local l=s.attr.sid; local o=a.eventable{ role="receiver"; peer=n.attr.from; sid=l; stream=e; }; setmetatable(o,t); local h; local r,d; for t in s:childtags()do if t.name=="content"and t.attr.xmlns==i then local i=t:child_with_name("description"); local a=i.attr.xmlns; if a then local e=e:event("jingle/content/"..a,o,i); if e then r=e; end end local a=t:child_with_name("transport"); local i=a.attr.xmlns; d=e:event("jingle/transport/"..i,o,a); if r and d then h=t; break; end end end if not r then e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified content is not supported")); return true; end if not d then e:send(a.error_reply(n,"cancel","feature-not-implemented","The specified transport is not supported")); return true; end e:send(a.reply(n)); o.content_tag=h; o.creator,o.name=h.attr.creator,h.attr.name; o.content,o.transport=r,d; function o:decline() end e:hook("jingle/"..l,function(e) if e.attr.from~=o.peer then return false; end local e=e:get_child("jingle",i); return o:handle_command(e); end); e:event("jingle",o); return true; end function t:handle_command(a) local t=a.attr.action; e:debug("Handling Jingle command: %s",t); if t=="session-terminate"then self:destroy(); elseif t=="session-accept"then self:handle_accepted(a); elseif t=="transport-info"then e:debug("Handling transport-info"); self.transport:info_received(a); elseif t=="transport-replace"then e:error("Peer wanted to swap transport, not implemented"); else e:warn("Unhandled Jingle command: %s",t); return nil; end return true; end function t:send_command(e,o,t) local e=a.iq({to=self.peer,type="set"}) :tag("jingle",{ xmlns=i, sid=self.sid, action=e, initiator=self.role=="initiator"and self.stream.jid or nil, responder=self.role=="responder"and self.jid or nil, }):add_child(o); if not t then self.stream:send(e); else self.stream:send_iq(e,t); end end function t:accept(t) local a=a.iq({to=self.peer,type="set"}) :tag("jingle",{ xmlns=i, sid=self.sid, action="session-accept", responder=e.jid, }) :tag("content",{creator=self.creator,name=self.name}); local o=self.content:generate_accept(self.content_tag:child_with_name("description"),t); a:add_child(o); local t=self.transport:generate_accept(self.content_tag:child_with_name("transport"),t); a:add_child(t); local t=self; e:send_iq(a,function(a) if a.attr.type=="error"then local a,t,a=a:get_error(); e:error("session-accept rejected: %s",t); return false; end t.transport:connect(function(a) e:warn("CONNECTED (receiver)!!!"); t.state="active"; t:event("connected",a); end); end); end e:hook("iq/"..i,u); return true; end function t:offer(t,o) local e=a.iq({to=self.peer,type="set"}) :tag("jingle",{xmlns=i,action="session-initiate", initiator=self.stream.jid,sid=self.sid}); e:tag("content",{creator=self.role,name=t}); local t=self.stream:event("jingle/describe/"..t,o); if not t then return false,"Unknown content type"; end e:add_child(t); local t=self.stream:event("jingle/transport/".."urn:xmpp:jingle:transports:s5b:1",self); self.transport=t; e:add_child(t:generate_initiate()); self.stream:debug("Hooking %s","jingle/"..self.sid); self.stream:hook("jingle/"..self.sid,function(e) if e.attr.from~=self.peer then return false; end local e=e:get_child("jingle",i); return self:handle_command(e) end); self.stream:send_iq(e,function(e) if e.attr.type=="error"then self.state="terminated"; local t,a,e=e:get_error(); return self:event("error",{type=t,condition=a,text=e}); end end); self.state="pending"; end function t:terminate(e) local e=a.stanza("reason"):tag(e or"success"); self:send_command("session-terminate",e,function(e) self.state="terminated"; self.transport:disconnect(); self:destroy(); end); end function t:destroy() self:event("terminated"); self.stream:unhook("jingle/"..self.sid,self.handle_command); end function t:handle_accepted(e) local e=e:child_with_name("transport"); self.transport:handle_accepted(e); self.transport:connect(function(e) self.stream:debug("CONNECTED (initiator)!") self.state="active"; self:event("connected",e); end); end function t:set_source(a,o) local function t() local e,i=a(); if e and e~=""then self.transport.conn:send(e); elseif e==""then return t(); elseif e==nil then if o then self:terminate(); end self.transport.conn:unhook("drained",t); a=nil; end end self.transport.conn:hook("drained",t); t(); end function t:set_sink(t) self.transport.conn:hook("incoming-raw",t); self.transport.conn:hook("disconnected",function(e) self.stream:debug("Closing sink..."); local e=e.reason; if e=="closed"then e=nil;end t(nil,e); end); end end) package.preload['verse.plugins.jingle_ft']=(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 a=require"ltn12"; local n=package.config:sub(1,1); local t="urn:xmpp:jingle:apps:file-transfer:4"; function o.plugins.jingle_ft(e) e:hook("ready",function() e:add_disco_feature(t); end,10); local i={type="file"}; function i:generate_accept(t,e) if e and e.save_file then self.jingle:hook("connected",function() local e=a.sink.file(io.open(e.save_file,"w+")); self.jingle:set_sink(e); end); end return t; end local i={__index=i}; e:hook("jingle/content/"..t,function(t,e) local e=e:get_child("file"); local e={ name=e:get_child_text("name"); size=tonumber(e:get_child_text("size")); desc=e:get_child_text("desc"); date=e:get_child_text("date"); }; return setmetatable({jingle=t,file=e},i); end); e:hook("jingle/describe/file",function(e) local a; if e.timestamp then a=os.date("!%Y-%m-%dT%H:%M:%SZ",e.timestamp); end return o.stanza("description",{xmlns=t}) :tag("file") :tag("name"):text(e.filename):up() :tag("size"):text(tostring(e.size)):up() :tag("date"):text(a):up() :tag("desc"):text(e.description):up() :up(); end); function e:send_file(i,t) local e,o=io.open(t); if not e then return e,o;end local o=e:seek("end",0); e:seek("set",0); local a=a.source.file(e); local e=self:jingle(i); e:offer("file",{ filename=t:match("[^"..n.."]+$"); size=o; }); e:hook("connected",function() e:set_source(a,true); end); return e; end end end) package.preload['verse.plugins.jingle_s5b']=(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="urn:xmpp:jingle:transports:s5b:1"; local l="http://jabber.org/protocol/bytestreams"; local h=require"util.hashes".sha1; local d=require"util.id".short; local function r(e,s) local function n() e:unhook("connected",n); return true; end local function o(t) e:unhook("incoming-raw",o); if t:sub(1,2)~="\005\000"then return e:event("error","connection-failure"); end e:event("connected"); return true; end local function i(a) e:unhook("incoming-raw",i); if a~="\005\000"then local t="version-mismatch"; if a:sub(1,1)=="\005"then t="authentication-failure"; end return e:event("error",t); end e:send(string.char(5,1,0,3,#s)..s.."\0\0"); e:hook("incoming-raw",o,100); return true; end e:hook("connected",n,200); e:hook("incoming-raw",i,100); e:send("\005\001\000"); end local function n(a,e,i) local e=t.new(nil,{ streamhosts=e, current_host=0; }); local function t(o) if o then return a(nil,o.reason); end if e.current_host<#e.streamhosts then e.current_host=e.current_host+1; e:debug("Attempting to connect to "..e.streamhosts[e.current_host].host..":"..e.streamhosts[e.current_host].port.."..."); local a,t=e:connect( e.streamhosts[e.current_host].host, e.streamhosts[e.current_host].port ); if not a then e:debug("Error connecting to proxy (%s:%s): %s", e.streamhosts[e.current_host].host, e.streamhosts[e.current_host].port, t ); else e:debug("Connecting..."); end r(e,i); return true; end e:unhook("disconnected",t); return a(nil); end e:hook("disconnected",t,100); e:hook("connected",function() e:unhook("disconnected",t); a(e.streamhosts[e.current_host],e); end,100); t(); return e; end function t.plugins.jingle_s5b(e) e:hook("ready",function() e:add_disco_feature(o); end,10); local a={}; function a:generate_initiate() self.s5b_sid=d(); local a=t.stanza("transport",{xmlns=o, mode="tcp",sid=self.s5b_sid}); local t=0; for o,i in pairs(e.proxy65.available_streamhosts)do t=t+1; a:tag("candidate",{jid=o,host=i.host, port=i.port,cid=o,priority=t,type="proxy"}):up(); end e:debug("Have %d proxies",t) return a; end function a:generate_accept(e) local a={}; self.s5b_peer_candidates=a; self.s5b_mode=e.attr.mode or"tcp"; self.s5b_sid=e.attr.sid or self.jingle.sid; for e in e:childtags()do a[e.attr.cid]={ type=e.attr.type; jid=e.attr.jid; host=e.attr.host; port=tonumber(e.attr.port)or 0; priority=tonumber(e.attr.priority)or 0; cid=e.attr.cid; }; end local e=t.stanza("transport",{xmlns=o}); return e; end function a:connect(i) e:warn("Connecting!"); local a={}; for t,e in pairs(self.s5b_peer_candidates or{})do a[#a+1]=e; end if#a>0 then self.connecting_peer_candidates=true; local function s(a,e) 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=a.cid})); self.onconnect_callback=i; self.conn=e; 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 a:info_received(a) e:warn("Info received"); 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 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(t.iq({to=i.jid,type="set"}) :tag("query",{xmlns=l,sid=self.s5b_sid}) :tag("activate"):text(self.jingle.peer),function(i) if i.attr.type=="result"then self.jingle:send_command("transport-info",t.stanza("content",s.attr) :tag("transport",{xmlns=o,sid=self.s5b_sid}) :tag("activated",{cid=a.attr.cid})); self.conn=e; self.onconnect_callback(e); else self.jingle.stream:error("Failed to activate bytestream"); end end); end end self.jingle.stream:debug("CID: %s",self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]); local t={ self.jingle.stream.proxy65.available_streamhosts[a.attr.cid]; }; 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 a:disconnect() if self.conn then self.conn:close(); end end function a:handle_accepted(e) end local t={__index=a}; e:hook("jingle/transport/"..o,function(e) return setmetatable({ role=e.role, peer=e.peer, stream=e.stream, jingle=e, },t); end); 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.id".short; local r=require"util.hashes".sha1; local n={}; n.__index=n; local i="http://jabber.org/protocol/bytestreams"; local s; function a.plugins.proxy65(t) t.proxy65=setmetatable({stream=t},n); t.proxy65.available_streamhosts={}; local e=0; t:hook("disco/service-discovered/proxy",function(o) if o.type=="bytestreams"then e=e+1; t:send_iq(a.iq({to=o.jid,type="get"}) :tag("query",{xmlns=i}),function(a) e=e-1; if a.attr.type=="result"then local e=a:get_child("query",i) :get_child("streamhost").attr; t.proxy65.available_streamhosts[e.jid]={ jid=e.jid; host=e.host; port=tonumber(e.port); }; end if e==0 then t:event("proxy65/discovered-proxies",t.proxy65.available_streamhosts); end end); end end); t:hook("iq/"..i,function(o) local e=a.new(nil,{ initiator_jid=o.attr.from, streamhosts={}, current_host=0; }); for t in o.tags[1]:childtags()do if t.name=="streamhost"then table.insert(e.streamhosts,t.attr); end end local function i() if e.current_host<#e.streamhosts then e.current_host=e.current_host+1; e:connect( e.streamhosts[e.current_host].host, e.streamhosts[e.current_host].port ); s(t,e,o.tags[1].attr.sid,o.attr.from,t.jid); return true; end e:unhook("disconnected",i); t:send(a.error_reply(o,"cancel","item-not-found")); end function e:accept() e:hook("disconnected",i,100); e:hook("connected",function() e:unhook("disconnected",i); local e=a.reply(o) :tag("query",o.tags[1].attr) :tag("streamhost-used",{jid=e.streamhosts[e.current_host].jid}); t:send(e); end,100); i(); end function e:refuse() end t:event("proxy65/request",e); end); end function n:new(t,n) local e=a.new(nil,{ target_jid=t; bytestream_sid=h(); }); local o=a.iq{type="set",to=t} :tag("query",{xmlns=i,mode="tcp",sid=e.bytestream_sid}); for t,e in ipairs(n or self.proxies)do o:tag("streamhost",e):up(); end self.stream:send_iq(o,function(o) if o.attr.type=="error"then local o,t,a=o:get_error(); e:event("connection-failed",{conn=e,type=o,condition=t,text=a}); 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 if t.jid==e.streamhost_jid then o,h=t.host,t.port; break; end end e:connect(o,h); local function o() e:unhook("connected",o); local t=a.iq{to=e.streamhost_jid,type="set"} :tag("query",{xmlns=i,sid=e.bytestream_sid}) :tag("activate"):text(t); self.stream:send_iq(t,function(t) if t.attr.type=="result"then e:event("connected",e); end end); return true; end e:hook("connected",o,100); s(self.stream,e,e.bytestream_sid,self.stream.jid,t); end end); return e; end function s(i,e,t,a,o) local i=r(t..a..o); local function s() e:unhook("connected",s); return true; end local function n(t) e:unhook("incoming-raw",n); if t:sub(1,2)~="\005\000"then return e:event("error","connection-failure"); end e:event("connected"); return true; end local function a(o) e:unhook("incoming-raw",a); if o~="\005\000"then local t="version-mismatch"; if o:sub(1,1)=="\005"then t="authentication-failure"; end return e:event("error",t); end e:send(string.char(5,1,0,3,#i)..i.."\0\0"); e:hook("incoming-raw",n,100); return true; end e:hook("connected",s,200); e:hook("incoming-raw",a,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.id".short; local n="urn:xmpp:jingle:transports:ibb:1"; local o="http://jabber.org/protocol/ibb"; assert(i.encode("This is a test.")=="VGhpcyBpcyBhIHRlc3Qu","Base64 encoding failed"); assert(i.decode("VGhpcyBpcyBhIHRlc3Qu")=="This is a test.","Base64 decoding failed"); local a={}; local h={__index=a}; local function r(t) local t=setmetatable({stream=t},h) t=e.eventable(t); return t; end function a:initiate(t,e,a) self.block=2048; self.stanza=a or'iq'; self.peer=t; self.sid=e or tostring(self):match("%x+$"); self.iseq=0; self.oseq=0; local e=function(e) return self:feed(e) end self.feeder=e; print("Hooking incoming IQs"); local t=self.stream; t:hook("iq/"..o,e) if a=="message"then t:hook("message",e) end end function a:open(t) self.stream:send_iq(e.iq{to=self.peer,type="set"} :tag("open",{ xmlns=o, ["block-size"]=self.block, sid=self.sid, stanza=self.stanza }) ,function(e) if t then if e.attr.type~="error"then t(true) else t(false,e:get_error()) end end end); end function a:send(n) local a=self.stanza; local t; if a=="iq"then t=e.iq{type="set",to=self.peer} elseif a=="message"then t=e.message{to=self.peer} end local e=self.oseq; self.oseq=e+1; t:tag("data",{xmlns=o,sid=self.sid,seq=e}) :text(i.encode(n)); if a=="iq"then self.stream:send_iq(t,function(e) self:event(e.attr.type=="result"and"drained"or"error"); end) else stream:send(t) self:event("drained"); end end function a:feed(t) if t.attr.from~=self.peer then return end local a=t[1]; if a.attr.sid~=self.sid then return end local n; if a.name=="open"then self:event("connected"); self.stream:send(e.reply(t)) return true elseif a.name=="data"then local o=t:get_child_text("data",o); local a=tonumber(a.attr.seq); local n=self.iseq; if o and a then if a~=n then self.stream:send(e.error_reply(t,"cancel","not-acceptable","Wrong sequence. Packet lost?")) self:close(); self:event("error"); return true; end self.iseq=a+1; local a=i.decode(o); if self.stanza=="iq"then self.stream:send(e.reply(t)) end self:event("incoming-raw",a); return true; end elseif a.name=="close"then self.stream:send(e.reply(t)) self:close(); return true end end function a:close() self.stream:unhook("iq/"..o,self.feeder) self:event("disconnected"); end function e.plugins.jingle_ibb(a) a:hook("ready",function() a:add_disco_feature(n); end,10); local t={}; function t:_setup() local e=r(self.stream); e.sid=self.sid or e.sid; e.stanza=self.stanza or e.stanza; e.block=self.block or e.block; e:initiate(self.peer,self.sid,self.stanza); self.conn=e; end function t:generate_initiate() print("ibb:generate_initiate() as "..self.role); local t=s(); self.sid=t; self.stanza='iq'; self.block=2048; local e=e.stanza("transport",{xmlns=n, sid=self.sid,stanza=self.stanza,["block-size"]=self.block}); return e; end function t:generate_accept(t) print("ibb:generate_accept() as "..self.role); local e=t.attr; self.sid=e.sid or self.sid; self.stanza=e.stanza or self.stanza; self.block=e["block-size"]or self.block; self:_setup(); return t; end function t:connect(t) if not self.conn then self:_setup(); end local e=self.conn; print("ibb:connect() as "..self.role); if self.role=="initiator"then e:open(function(a,...) assert(a,table.concat({...},", ")); t(e); end); else t(e); end end function t:info_received(e) print("ibb:info_received()"); end function t:disconnect() if self.conn then self.conn:close() end end function t:handle_accepted(e)end local t={__index=t}; a:hook("jingle/transport/"..n,function(e) return setmetatable({ role=e.role, peer=e.peer, stream=e.stream, jingle=e, },t); end); 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 n=table.insert; local o="http://jabber.org/protocol/pubsub"; local h="http://jabber.org/protocol/pubsub#owner"; local a="http://jabber.org/protocol/pubsub#event"; local t={}; local s={__index=t}; function i.plugins.pubsub(e) e.pubsub=setmetatable({stream=e},s); e:hook("message",function(t) local o=t.attr.from; for t in t:childtags("event",a)do local t=t:get_child("items"); if t then local a=t.attr.node; for t in t:childtags("item")do e:event("pubsub/event",{ from=o; node=a; item=t; }); end for t in t:childtags("retract")do e:event("pubsub/retraction",{ from=o; node=a; item=t; }); end end end end); return true; end function t:create(a,t,e) return self:service(a):node(t):create(nil,e); end function t:subscribe(e,t,o,a) return self:service(e):node(t):subscribe(o,nil,a); end function t:publish(i,o,t,a,e) return self:service(i):node(o):publish(t,nil,a,e); end local a={}; local r={__index=a}; function t:service(e) return setmetatable({stream=self.stream,service=e},r) end local function t(t,r,s,a,d,h,e,n) local t=i.iq{type=t or"get",to=r} :tag("pubsub",{xmlns=s or o}) local o={node=d,jid=h}; if n then for t,e in pairs(n)do o[t]=e; end end if a then t:tag(a,o);end if e then t:tag("item",{id=e~=true and e or nil}); end return t; end function a:subscriptions(e) self.stream:send_iq(t(nil,self.service,nil,"subscriptions") ,e and function(a) if a.attr.type=="result"then local t=a:get_child("pubsub",o); local t=t and t:get_child("subscriptions"); local a={}; if t then for e in t:childtags("subscription")do local t=self:node(e.attr.node) t.subscription=e; t.subscribed_jid=e.attr.jid; n(a,t); end end e(a); else e(false,a:get_error()); end end or nil); end function a:affiliations(a) self.stream:send_iq(t(nil,self.service,nil,"affiliations") ,a and function(e) if e.attr.type=="result"then local e=e:get_child("pubsub",o); local e=e and e:get_child("affiliations")or{}; local o={}; if e then for t in e:childtags("affiliation")do local e=self:node(t.attr.node) e.affiliation=t; n(o,e); end end a(o); else a(false,e:get_error()); end end or nil); end function a:nodes(a) self.stream:disco_items(self.service,nil,function(e,...) if e then for t=1,#e do e[t]=self:node(e[t].node); end end a(e,...) end); end local e={}; local o={__index=e}; function a:node(e) return setmetatable({stream=self.stream,service=self.service,node=e},o) end function s:__call(t,e) local t=self:service(t); return e and t:node(e)or t; end function e:hook(a,o) self._hooks=self._hooks or setmetatable({},{__mode='kv'}); local function t(e) if(not e.service or e.from==self.service)and e.node==self.node then return a(e) end end self._hooks[a]=t; self.stream:hook("pubsub/event",t,o); return t; end function e:unhook(e) if e then local e=self._hooks[e]; self.stream:unhook("pubsub/event",e); elseif self._hooks then for e in pairs(self._hooks)do self.stream:unhook("pubsub/event",e); end end end 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),e); end end function e:configure(e,a) if e~=nil then error("Not implemented yet."); end self.stream:send_iq(t("set",self.service,nil,e==nil and"default"or"configure",self.node),a); end function e:publish(e,a,o,i) if a~=nil then error("Node configuration is not implemented yet."); end self.stream:send_iq(t("set",self.service,nil,"publish",self.node,nil,e or true) :add_child(o) ,i); end function e:subscribe(e,o,a) e=e or self.stream.jid; if o~=nil then error("Subscription configuration is not implemented yet."); end self.stream:send_iq(t("set",self.service,nil,"subscribe",self.node,e) ,a); end function e:subscription(e) error("Not implemented yet."); end function e:affiliation(e) error("Not implemented yet."); end function e:unsubscribe(e,a) e=e or self.subscribed_jid or self.stream.jid; self.stream:send_iq(t("set",self.service,nil,"unsubscribe",self.node,e) ,a); end function e:configure_subscription(e,e) error("Not implemented yet."); end function e:items(t,e) if t then return self:item(nil,e); else self.stream:disco_items(self.service,self.node,e); end end function e:item(a,e) self.stream:send_iq(t("get",self.service,nil,"items",self.node,nil,a) ,e); end function e:retract(o,e,a) if type(e)=="function"then e,a=false,e; end self.stream:send_iq( t( "set", self.service, nil, "retract", self.node, nil, o, {notify=e and"1"or nil} ), a ); end function e:purge(e,a) self.stream:send_iq( t( "set", self.service, h, "purge", self.node, nil, nil, {notify=e and"1"or nil} ), a ); end function e:delete(a,e) assert(not a,"Not implemented yet."); 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 t=require"verse"; local e="http://jabber.org/protocol/pubsub"; local e=e.."#event"; function t.plugins.pep(e) e:add_plugin("disco"); e:add_plugin("pubsub"); e.pep={}; e:hook("pubsub/event",function(t) return e:event("pep/"..t.node,{from=t.from,id=t.item.attr.id,item=t.item.tags[1]}); end); function e:hook_pep(t,o,i) local a=e.events._handlers["pep/"..t]; if not(a)or#a==0 then e:add_disco_feature(t.."+notify"); end e:hook("pep/"..t,o,i); end function e:unhook_pep(t,a) e:unhook("pep/"..t,a); local a=e.events._handlers["pep/"..t]; if not(a)or#a==0 then e:remove_disco_feature(t.."+notify"); end end 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"; local r="jabber:x:data"; local a={}; a.__index=a; local i={}; function o.plugins.adhoc(e) e:add_plugin("disco"); e:add_disco_feature(t); function e:query_commands(a,o) e:disco_items(a,t,function(a) e:debug("adhoc list returned") local t={}; for o,a in ipairs(a)do t[a.node]=a.name; end e:debug("adhoc calling callback") return o(t); end); end function e:execute_command(i,o,t) local e=setmetatable({ stream=e,jid=i, command=o,callback=t },a); return e:execute(); end local function r(t,e) if not(e)or e=="user"then return true;end if type(e)=="function"then return e(t); end end function e:add_adhoc_command(o,a,h,s) i[a]=n.new(o,a,h,s); e:add_disco_item({jid=e.jid,node=a,name=o},t); return i[a]; end local function s(t) local a=t.tags[1]; local a=a.attr.node; local a=i[a]; if not a then return;end if not r(t.attr.from,a.permission)then e:send(o.error_reply(t,"auth","forbidden","You don't have permission to execute this command"):up() :add_child(a:cmdtag("canceled") :tag("note",{type="error"}):text("You don't have permission to execute this command"))); return true end return n.handle_cmd(a,{send=function(t)return e:send(t)end},t); end e:hook("iq/"..t,function(e) local a=e.attr.type; local t=e.tags[1].name; if a=="set"and t=="command"then return s(e); end end); end function a:_process_response(e) if e.attr.type=="error"then self.status="canceled"; self.callback(self,{}); return; end local e=e:get_child("command",t); self.status=e.attr.status; self.sessionid=e.attr.sessionid; self.form=e:get_child("x",r); self.note=e:get_child("note"); self.callback(self); end function a:execute() local e=o.iq({to=self.jid,type="set"}) :tag("command",{xmlns=t,node=self.command}); self.stream:send_iq(e,function(e) self:_process_response(e); end); end function a:next(a) local e=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) 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"; local o=require"util.stanza"; function a.plugins.presence(t) t.last_presence=nil; t:hook("presence-out",function(e) if not e.attr.to then t.last_presence=o.clone(e); end end,1); function t:resend_presence() if self.last_presence then t:send(self.last_presence); end end function t:set_status(e) local a=a.presence(); if type(e)=="table"then if e.show then a:tag("show"):text(e.show):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 _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(n) function n:private_set(o,i,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 t:add_child(e); else t:tag(o,{xmlns=i}) :add_child(e); end end self:send_iq(t,n); end function n:private_get(e,o,i) self:send_iq(a.iq({type="get"}) :tag("query",{xmlns=t}) :tag(e,{xmlns=o}), function(a) if a.attr.type=="result"then local t=a:get_child("query",t); local e=t:get_child(e,o); i(e); end end); end 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 a="jabber:iq:roster"; local n="urn:xmpp:features:rosterver"; local o=table.insert; function i.plugins.roster(t) local s=false; local e={ items={}; ver=""; }; t.roster=e; t:hook("stream-features",function(e) if e:get_child("ver",n)then s=true; end end); local function h(t) local e=i.stanza("item",{xmlns=a}); for a,t in pairs(t)do if a~="groups"then e.attr[a]=t; else for a=1,#t do e:tag("group"):text(t[a]):up(); end end end return e; end local function r(a) local e={}; 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 a:childtags("group")do o(t,e:get_text()) end return e; end function e:load(t) e.ver,e.items=t.ver,t.items; end function e:dump() return{ ver=e.ver, items=e.items, }; end function e:add_contact(o,n,s,e) local o={jid=o,name=n,groups=s}; local a=i.iq({type="set"}) :tag("query",{xmlns=a}) :add_child(h(o)); t:send_iq(a,function(t) if not e then return end if t.attr.type=="result"then e(true); else e(nil,t); end end); end function e:delete_contact(o,n) o=(type(o)=="table"and o.jid)or o; local s={jid=o,subscription="remove"} if not e.items[o]then return false,"item-not-found";end t:send_iq(i.iq({type="set"}) :tag("query",{xmlns=a}) :add_child(h(s)), function(e) if not n then return end if e.attr.type=="result"then n(true); else n(nil,e); end end); end local function h(t) local t=r(t); e.items[t.jid]=t; end local function r(t) local a=e.items[t]; e.items[t]=nil; return a; end function e:fetch(n) t:send_iq(i.iq({type="get"}):tag("query",{xmlns=a,ver=s and e.ver or nil}), function(o) if o.attr.type=="result"then local t=o:get_child("query",a); if t then e.items={}; for t in t:childtags("item")do h(t) end e.ver=t.attr.ver or""; end n(e); else n(nil,o); 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 a=s and s:get_child("item"); if a then local n,o; local i=a.attr.jid; if a.attr.subscription=="remove"then n="removed" o=r(i); else n=e.items[i]and"changed"or"added"; h(a) o=e.items[i]; end e.ver=s.attr.ver; if o then t:event("roster/item-"..n,o); end end t:send(i.reply(n)) 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 i="jabber:iq:register"; function t.plugins.register(e) 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=i}) :tag("username"):text(e.username):up() :tag("password"):text(e.password):up(); if e.register_email then t:tag("email"):text(e.register_email):up(); end e:send_iq(t,function(t) if t.attr.type=="result"then e:event("registration-success"); else local a,t,o=t:get_error(); e:debug("Registration failed: %s",t); e:event("registration-failure",{type=a,condition=t,text=o}); end end); else e:debug("In-band registration not offered by server"); e:event("registration-failure",{condition="service-unavailable"}); end e:unhook("stream-features",a); return true; end e:hook("stream-features",a,310); 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"util.events"; local n=require"util.jid"; local a={}; a.__index=a; local h="urn:xmpp:delay"; local s="http://jabber.org/protocol/muc"; function i.plugins.groupchat(o) o:add_plugin("presence") o.rooms={}; o:hook("stanza",function(e) local a=n.bare(e.attr.from); if not a then return end local t=o.rooms[a] if not t and e.attr.to and a then t=o.rooms[e.attr.to.." "..a] 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 n=e:get_child_text("body"); local o=e:get_child("delay",h); local a={ room_jid=a; room=t; sender=t.occupants[i]; nick=i; body=n; stanza=e; delay=(o and o.attr.stamp); }; local t=t:event(e.name,a); return t or(e.name=="message")or nil; end end,500); function o:join_room(n,h,t,r) if not h then return false,"no nickname supplied" end t=t or{}; local e=setmetatable(i.eventable{ stream=o,jid=n,nick=h, subject=nil, occupants={}, opts=t, },a); if t.source then self.rooms[t.source.." "..n]=e; else self.rooms[n]=e; end local a=e.occupants; e:hook("presence",function(o) local t=o.nick or h; if not a[t]and o.stanza.attr.type~="unavailable"then a[t]={ nick=t; jid=o.stanza.attr.from; presence=o.stanza; }; local o=o.stanza:get_child("x",s.."#user"); if o then local e=o:get_child("item"); if e and e.attr then a[t].real_jid=e.attr.jid; a[t].affiliation=e.attr.affiliation; a[t].role=e.attr.role; end end if t==e.nick then e.stream:event("groupchat/joined",e); else e:event("occupant-joined",a[t]); end elseif a[t]and o.stanza.attr.type=="unavailable"then if t==e.nick then e.stream:event("groupchat/left",e); if e.opts.source then self.rooms[e.opts.source.." "..n]=nil; else self.rooms[n]=nil; end else a[t].presence=o.stanza; e:event("occupant-left",a[t]); a[t]=nil; end end end); e:hook("message",function(a) local t=a.stanza:get_child_text("subject"); if not t then return end t=#t>0 and t or nil; if t~=e.subject then local o=e.subject; e.subject=t; return e:event("subject-changed",{from=o,to=t,by=a.sender,event=a}); end end,2e3); local t=i.presence():tag("x",{xmlns=s}):reset(); if r then t:get_child("x",s):tag("password"):text(r):reset(); end self:event("pre-groupchat/joining",t); e:send(t) self:event("groupchat/joining",e); return e; end o:hook("presence-out",function(e) if not e.attr.to then for a,t in pairs(o.rooms)do t:send(e); end e.attr.to=nil; end end); end function a:send(e) if e.name=="message"and not e.attr.type then e.attr.type="groupchat"; end if e.name=="presence"then e.attr.to=self.jid.."/"..self.nick; end if e.attr.type=="groupchat"or not e.attr.to then e.attr.to=self.jid; end if self.opts.source then e.attr.from=self.opts.source end self.stream:send(e); end function a:send_message(e) self:send(i.message():tag("body"):text(e)); end function a:set_subject(e) self:send(i.message():tag("subject"):text(e)); end function a:leave(e) self.stream:event("groupchat/leaving",self); local t=i.presence({type="unavailable"}); if e then t:tag("status"):text(e); end self:send(t); end function a:admin_set(t,o,a,e) self:send(i.iq({type="set"}) :query(s.."#admin") :tag("item",{nick=t,[o]=a}) :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(e,t,a) self:admin_set(e,"affiliation",t,a); end function a:kick(t,e) self:set_role(t,"none",e); end function a:ban(t,e) self:set_affiliation(t,"outcast",e); 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"; function i.plugins.vcard(a) function a:get_vcard(n,t) a:send_iq(i.iq({to=n,type="get"}) :tag("vCard",{xmlns=e}),t and function(a) local e=a:get_child("vCard",e); if a.attr.type=="result"and e then e=o.from_xep54(e) t(e) else t(false) end end or nil); end function a:set_vcard(e,n) local t; if type(e)=="table"and e.name then t=e; elseif type(e)=="string"then t=o.to_xep54(o.from_text(e)[1]); elseif type(e)=="table"then t=o.to_xep54(e); error("Converting a table to vCard not implemented") end if not t then return false end a:debug("setting vcard to %s",tostring(t)); a:send_iq(i.iq({type="set"}) :add_child(t),n); end 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 i=require"verse"; local n="vcard-temp:x:update"; local s=require("util.hashes").sha1; local e,t=pcall(function() local e=require("util.encodings").base64.decode; assert(e("SGVsbG8=")=="Hello") return e; end); if not e then e,t=pcall(function()return require("mime").unb64;end); if not e then error("Could not find a base64 decoder") end end local h=t; function i.plugins.vcard_update(e) e:add_plugin("vcard"); e:add_plugin("presence"); local t; 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 o then local a=s(h(o),true); t=i.stanza("x",{xmlns=n}) :tag("photo"):text(a); e:resend_presence() else t=nil; end end local a; e:hook("ready",function() if a then return;end a=true; e:get_vcard(nil,function(t) if t then r(t) end e:event("ready"); end); return true; end,3); e:hook("presence-out",function(e) if t and not e:get_child("x",n)then e:add_child(t); end end,10); end end) package.preload['verse.plugins.carbons']=(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 o="urn:xmpp:carbons:2"; local r="urn:xmpp:forward:0"; local s=os.time; local h=require"util.datetime".parse; local n=require"util.jid".bare; function a.plugins.carbons(e) local t={}; t.enabled=false; e.carbons=t; function t:enable(i) e:send_iq(a.iq{type="set"} :tag("enable",{xmlns=o}) ,function(e) local e=e.attr.type=="result"; if e then t.enabled=true; end if i then i(e); end end or nil); end function t:disable(i) e:send_iq(a.iq{type="set"} :tag("disable",{xmlns=o}) ,function(e) local e=e.attr.type=="result"; if e then t.enabled=false; end if i then i(e); end end or nil); end local a; e:hook("bind-success",function() a=n(e.jid); end); e:hook("message",function(i) local t=i:get_child(nil,o); if i.attr.from==a and t then local o=t.name; local t=t:get_child("forwarded",r); local a=t and t:get_child("message","jabber:client"); local t=t:get_child("delay","urn:xmpp:delay"); local t=t and t.attr.stamp; t=t and h(t); if a then return e:event("carbon",{ dir=o, stanza=a, timestamp=t or s(), }); end end end,1); 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:2" local l="urn:xmpp:forward:0"; local d="urn:xmpp:delay"; local r=require"util.id".short; local c=require"util.datetime".parse; local s=require"util.datetime".datetime; local o=require"util.dataforms".new; local n=require"util.rsm"; local u={}; local m=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(i) function i:query_archive(i,a,h) local o=r(); local i=t.iq{id=o,type="set",to=i} :tag("query",{xmlns=e,queryid=o}); local t,r=tonumber(a["start"]),tonumber(a["end"]); a["start"]=t and s(t); a["end"]=r and s(r); i:add_child(m:form(a,"submit")); i:add_child(n.generate(a)); local t={}; local function s(a) local e=a:get_child("result",e); if e and e.attr.queryid==o then local a=e:get_child("forwarded",l); local i=e.attr.id; local e=a:get_child("delay",d); local o=e and c(e.attr.stamp)or nil; local e=a:get_child("message","jabber:client") t[#t+1]={id=i,stamp=o,message=e}; return true end end self:hook("message",s,1); self:send_iq(i,function(a) self:unhook("message",s); if a.attr.type=="error"then self:warn(table.concat({a:get_error()}," ")) h(false,a:get_error()) return true; end local e=a:get_child("fin",e) if e then t.complete=e.attr.complete=="true"or e.attr.complete=="1"; local e=n.get(e); for a,e in pairs(e or u)do t[a]=e;end end h(t); return true end); end local n={ always=true,[true]="always", never=false,[false]="never", roster="roster", } local function h(t) local e={}; local a=t.attr.default; if a then e[false]=n[a]; end local a=t:get_child("always"); if a then for t in a:childtags("jid")do local t=t:get_text(); e[t]=true; end end local t=t:get_child("never"); if t then for t in t:childtags("jid")do local t=t:get_text(); e[t]=false; end end return e; end local function s(o) local a a,o[false]=o[false],nil; if a~=nil then a=n[a]; end local a=t.stanza("prefs",{xmlns=e,default=a}) local e=t.stanza("always"); local t=t.stanza("never"); for o,a in pairs(o)do (a and e or t):tag("jid"):text(o):up(); end return a:add_child(e):add_child(t); end 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]); a(t,e); else a(nil,e); end end); end function i:archive_prefs_set(e,a) self:send_iq(t.iq{type="set"}:add_child(s(e)),a); end end end) package.preload['verse.plugins.browsing']=(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 o="urn:xmpp:browsing:0"; function a.plugins.browsing(e) e:add_plugin("pep"); function e:browsing(t,i) if type(t)=="string"then t={uri=t}; end local a=a.stanza("page",{xmlns=o}) for t,e in pairs(t)do a:tag(t):text(e):up(); end return e:publish_pep(a,i); end e:hook_pep(o,function(a) local t=a.item; return e:event("browsing",{ from=a.from; description=t:get_child_text"description"; keywords=t:get_child_text"keywords"; title=t:get_child_text"title"; uri=t:get_child_text"uri"; }); end); end end) package.preload['util.http']=(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,t=string.format,string.char; local h,r=pairs,ipairs; local o,s=table.insert,table.concat; local e={}; for a=0,255 do local t=t(a); local a=i("%%%02x",a); e[t]=a; e[a]=t; e[a:upper()]=t; end local function n(t) return t and(t:gsub("[^a-zA-Z0-9.~_-]",e)); end local function a(t) return t and(t:gsub("%%%x%x",e)); end local function e(e) return e and(n(e):gsub("%%20","+")); end local function d(t) local a={}; if t[1]then for i,t in r(t)do o(a,e(t.name).."="..e(t.value)); end else for i,t in h(t)do o(a,e(i).."="..e(t)); end end return s(a,"&"); end local function s(e) if not e:match("=")then return a(e);end local i={}; for e,t in e:gmatch("([^=&]*)=([^&]*)")do e,t=e:gsub("%+","%%20"),t:gsub("%+","%%20"); e,t=a(e),a(t); o(i,{name=e,value=t}); i[e]=t; end return i; end local function o(e,t) e=","..e:gsub("[ \t]",""):lower()..","; return e:find(","..t:lower()..",",1,true)~=nil; end local function t(e,t) if t then if e:sub(-1,-1)~="/"then e=e.."/";end else if e:sub(-1,-1)=="/"then e=e:sub(1,-2);end end if e:sub(1,1)~="/"then e="/"..e;end return e; end return{ urlencode=n,urldecode=a; formencode=d,formdecode=s; contains_token=o; normalize_path=t; }; end) package.preload['net.http.parser']=(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 l=tonumber; local b=assert; local q=require"socket.url".parse; local t=require"util.http".urldecode; local g=require"util.dbuffer"; local function j(e) e=t((e:gsub("//+","/"))); if e:sub(1,1)~="/"then e="/"..e; end local t=0; for e in e:gmatch("([^/]+)/")do if e==".."then t=t-1; elseif e~="."then t=t+1; end if t<0 then return nil; end end return e; end local k={}; function k.new(c,n,e,r) local f=true; if not e or e=="server"then f=false;else b(e=="client","Invalid parser type");end local w=l(r and r().body_size_limit)or 10*1024*1024; local s=l(r and r().head_size_limit)or 10*1024; local k=l(r and r().buffer_size_limit)or w*2; local a=g.new(k); local d; local h=nil; local e; local t; local y; local o; return{ feed=function(u,i) if o then return nil,"parse has failed";end if not i then if h and f and not t then a:collapse(); e.body=a:read_chunk()or""; e.partial=nil; c(e); h=nil; elseif a:length()~=0 then o=true;return n("unexpected-eof"); end return; end if not a:write(i)then o=true;return n("max-buffer-size-exceeded");end while a:length()>0 do if h==nil then local b=a:sub(1,s):find("\r\n\r\n",nil,true); if not b then return;end local p,u,m,s,x; local v; local i={}; for t in a:read(b+3):gmatch("([^\r\n]+)\r\n")do if v then local e,t=t:match("^([^%s:]+): *(.*)$"); if not e then o=true;return n("invalid-header-line");end e=e:lower(); i[e]=i[e]and i[e]..","..t or t; else v=t; if f then m,s,x=t:match("^HTTP/(1%.[01]) (%d%d%d) (.*)$"); s=l(s); if not s then o=true;return n("invalid-status-line");end y=not ((r and r().method=="HEAD") or(s==204 or s==304 or s==301) or(s>=100 and s<200)); else p,u,m=t:match("^(%w+) (%S+) HTTP/(1%.[01])$"); if not p then o=true;return n("invalid-status-line");end end end end if not v then o=true;return n("invalid-status-line");end d=y and i["transfer-encoding"]=="chunked"; t=l(i["content-length"]); if f then if not y then t=0;end e={ code=s; httpversion=m; headers=i; body=false; body_length=t; chunked=d; partial=true; responseversion=m; responseheaders=i; }; else local a; if u:byte()==47 then local t,e=u:match("([^?]*).?(.*)"); if e==""then e=nil;end a={path=t,query=e}; else a=q(u); if not(a and a.path)then o=true;return n("invalid-url");end end u=j(a.path); i.host=a.host or i.host; t=t or 0; e={ method=p; url=a; path=u; httpversion=m; headers=i; body=false; body_sink=nil; chunked=d; partial=true; }; end if not t or t>w then c(e); if not e.body_sink and(t and t>w)then o=true; return n("content-length-limit-exceeded"); end end if d and not e.body_sink then c(e); if not e.body_sink then e.body_buffer=g.new(k); end end h=true; end if h then if d then local s=a:sub(1,512); local t,i=s:match("^(%x+)[^\r\n]*\r\n()"); if not t then return;end t=t and l(t,16); if not t then o=true;return n("invalid-chunk-size");end if t==0 and s:find("\r\n\r\n",i-2,true)then local t=e.body_buffer; if t then e.body_buffer=nil; t:collapse(); e.body=t:read_chunk()or""; end a:collapse(); local t=a:read_chunk(); t=t:gsub("^.-\r\n\r\n",""); a:write(t); h,d=nil,nil; e.partial=nil; c(e); elseif a:length()-i-1>=t then a:discard(i-1); (e.body_sink or e.body_buffer):write(a:read(t)); a:discard(2); else break; end elseif e.body_sink then local i=a:read_chunk(t); while i and(not t or t>0)do if e.body_sink:write(i)then if t then t=t-#i; end i=a:read_chunk(t); else o=true; return n("body-sink-write-failure"); end end if t==0 then h=nil; e.partial=nil; c(e); end elseif not t or a:length()>=t then b(not d) e.body=t and a:read(t)or a:read_chunk()or""; h=nil; e.partial=nil; c(e); else break; end else break; end end end; }; end return k; end) package.preload['net.http']=(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 z=require"util.encodings".base64.encode; local T=require"socket.url" local _=require"net.http.parser".new; local d=require"util.http"; local O=require"util.events"; local A=require"util.x509".verify_identity; local E=require"util.promise"; local j=require"net.http.errors"; local x=require"net.resolvers.basic"; local q=require"net.connect".connect; local v=pcall(require,"ssl"); local s,p=table.insert,table.concat; local l=pairs; local y,c,t= tonumber,tostring,debug.traceback; local w=os.time; local b=require"util.xpcall".xpcall; local f=error local o=require"util.logger".init("http"); local _ENV=nil; local a={}; local function g(e)return(c(e):match("%x+$"));end local n={default_port=80,default_mode="*a"}; local function k(e)o("error","Traceback[http]: %s",t(c(e),2));return e;end local function m(e,t,...) if not t then o("error","Request '%s': error in callback: %s",e.id,(...)); if not e.suppress_errors then f(...); end end return...; end local function r(e) local t=e.conn; if t then e.conn=nil; t:close() end end local function u(e,t) if e.callback then e.callback(t or"cancelled",0,e); e.callback=nil; end if e.conn then r(e); end end local function h(e,n,a) if not e.parser then local function i(t) if e.callback then e.callback(t or"connection-closed",0,e); e.callback=nil; end r(e); end if not n then i(a); return; end local a; local function n(t) if t.partial then o("debug","Request '%s': partial response (%s%s)", e.id, t.chunked and"chunked, "or"", t.body_length and("%d bytes"):format(t.body_length)or"unknown length" ); if e.streaming_handler then o("debug","Request '%s': Streaming via handler",e.id); t.body_sink,a=e.streaming_handler(t); end return; elseif a then o("debug","Request '%s': Finalizing response stream"); a(t); end if e.callback then e.callback(t.body,t.code,t,e); e.callback=nil; end r(e); end local function t() return e; end e.parser=_(n,i,"client",t); end e.parser:feed(n); end function n.onconnect(t) local e=a[t]; e.write=function(...)return e.conn:write(...);end local r=e.callback; e.callback=function(n,a,i,s) do local t={http=e.http,url=e.url,request=e,response=i,content=n,code=a,callback=e.callback}; e.http.events.fire_event("response",t); n,a,i=t.content,t.code,t.response; end o("debug","Request '%s': Calling callback, status %s",e.id,a or"---"); return m(e.id,b(r,k,n,a,i,s)); end e.reader=h; e.state="status"; e.cancel=u; a[e.conn]=e; if not e.insecure and t:ssl()then local a=t:socket(); local o=a.getpeerverification and a:getpeerverification(); if not o then e.callback("certificate-chain-invalid",0,e); e.callback=nil; t:close(); return; end local a=a.getpeercertificate and a:getpeercertificate(); if not a or not A(e.host,false,a)then e.callback("certificate-verify-failed",0,e); e.callback=nil; t:close(); return; end end local a={e.method or"GET"," ",e.path," HTTP/1.1\r\n"}; if e.query then s(a,4,"?"..e.query); end for e,t in l(e.headers)do s(a,e..": "..t.."\r\n"); end s(a,"\r\n") t:write(p(a)); if e.body then t:write(e.body); end end function n.onincoming(i,t) local e=a[i]; if not e then o("warn","Received response from connection %s with no request attached!",i); return; end if t and e.reader then e:reader(t); end end function n.ondisconnect(t,o) local e=a[t]; if e and e.conn then e:reader(nil,o or"closed"); end a[t]=nil; end function n.onattach(e,t) a[e]=t; t.conn=e; end function n.ondetach(e) a[e]=nil; end function n.onfail(e,t) e.http.events.fire_event("request-connection-error",{http=e.http,request=e,url=e.url,err=t}); e.callback(t or"connection failed",0,e); end local function m(a,h,t,i) local e=T.parse(h); if not(e and e.host)then i("invalid-url",0,e); return nil,"invalid-url"; end e.url=h; e.http=a; e.time=w(); if not e.path then e.path="/"; end e.id=t and t.id or g(e); do local o={http=a,url=h,request=e,options=t,callback=i}; local a=a.events.fire_event("pre-request",o); if a then return a; end e,h,t,e.callback=o.request,o.url,o.options,o.callback; end local u,s,d; local m,i=e.host,e.port; local r=m; if(i=="80"and e.scheme=="http") or(i=="443"and e.scheme=="https")then i=nil; elseif i then r=r..":"..i; end s={ ["Host"]=r; ["User-Agent"]="Prosody XMPP Server"; }; if e.userinfo then s["Authorization"]="Basic "..z(e.userinfo); end if t then e.onlystatus=t.onlystatus; d=t.body; if d then u="POST"; s["Content-Length"]=c(#d); s["Content-Type"]="application/x-www-form-urlencoded"; end if t.method then u=t.method;end if t.headers then for e,t in l(t.headers)do s[e]=t; end end e.insecure=t.insecure; e.suppress_errors=t.suppress_errors; e.streaming_handler=t.streaming_handler; end o("debug","Making %s %s request '%s' to %s",e.scheme:upper(),u or"GET",e.id,(t and t.suppress_url and r)or h); e.method,e.headers,e.body=u,s,d; local o=e.scheme=="https"; if o and not v then f("SSL not available, unable to contact https URL"); end local r=i and y(i)or(o and 443 or 80); local s=a.options and a.options.use_dane; local i=false; if o then i=t and t.sslctx or a.options and a.options.sslctx; if t and t.use_dane~=nil then s=t.use_dane; end end local t=x.new(m,r,"tcp",{servername=e.host;use_dane=s}); q(t,n,{sslctx=i},e); a.events.fire_event("request",{http=a,request=e,url=h}); return e; end local function e(a) local e={ options=a; request=function(a,t,e,o) if o~=nil then return m(a,t,e,o); else return E.new(function(n,o) m(a,t,e,function(i,a,e,t) if a==0 then o(j.new(i,{request=e})); else e.request=t; n(e); end end); end); end end; new=a and function(o) local t={}; for e,a in l(a)do t[e]=a;end if o then for a,e in l(o)do t[a]=e;end end return e(t); end or e; events=O.new(); }; return e; end local t=e({ sslctx={mode="client",protocol="sslv23",options={"no_sslv2","no_sslv3"},alpn="http/1.1",verify="peer"}; suppress_errors=true; }); return{ request=function(o,a,e) return t:request(o,a,e); end; default=t; new=e; events=t.events; urlencode=d.urlencode; urldecode=d.urldecode; formencode=d.formencode; formdecode=d.formdecode; destroy_request=r; features={ sni=true; }; }; end) package.preload['util.x509']=(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"util.encodings".stringprep.nameprep; local r=require"util.encodings".idna.to_ascii; local f=require"util.encodings".idna.to_unicode; local c=require"util.encodings".base64; local e=require"util.logger".init("x509"); local y=require"util.multitable"; local p=string.format; local d=ipairs; local _ENV=nil; local u="2.5.4.3"; local h="2.5.29.17"; local i="1.3.6.1.5.5.7.8.5"; local n="1.3.6.1.5.5.7.8.7"; local function l(o,a) local t=r(o) if t==nil then e("info","Host %s failed IDNA ToASCII operation",o) return false end t=t:lower() local i=t:gsub("^[^.]+%.","") for o=1,#a do local a=a[o] if t==a:lower()then e("debug","Cert dNSName %s matched hostname",a); return true end if a:match("^%*%.")then local t=a:gsub("^[^.]+%.","") if i==t:lower()then e("debug","Cert dNSName %s matched hostname",a); return true end end end return false end local function w(a,t) local i=o(a) for a=1,#t do local t=t[a] if t:match("[@/]")then e("debug","Ignoring xmppAddr %s because it's not a bare domain",t) else local a=o(t) if a==nil then e("info","Ignoring xmppAddr %s, failed nameprep!",t) else if i==a then e("debug","Cert xmppAddr %s matched hostname",t) return true end end end end return false end local function m(i,o,t) local a=r(i) if a==nil then e("info","Host %s failed IDNA ToASCII operation",i); return false end if o:match("^_")==nil then o="_"..o end a=a:lower(); local n=a:gsub("^[^.]+%.","") for i=1,#t do local i,t=t[i]:match("^(_[^.]+)%.(.*)"); if o==i then if a==t:lower()then e("debug","Cert SRVName %s matched hostname",t); return true; end if t:match("^%*%.")then local a=t:gsub("^[^.]+%.","") if n==a:lower()then e("debug","Cert SRVName %s matched hostname",t) return true end end if a==t:lower()then e("debug","Cert SRVName %s matched hostname",t); return true end end end return false end local function v(a,o,s) if s.setencode then s:setencode("utf8"); end local t=s:extensions() if t[h]then local e=t[h]; local t=false if e[i]then t=true if o=="_xmpp-client"or o=="_xmpp-server"then if w(a,e[i])then return true end end end if e[n]then t=true if o and m(a,o,e[n])then return true end end if e["dNSName"]then t=true if l(a,e["dNSName"])then return true end end if e["uniformResourceIdentifier"]then t=true end if t then return false end end local o=s:subject() local t=nil for a=1,#o do local a=o[a] if a["oid"]==u then if t then e("info","Certificate has multiple common names") return false end t=a["value"]; end end if t then return l(a,{t}) end return false end local function l(s) if s.setencode then s:setencode("utf8"); end local a=y.new(); local e=s:extensions(); local t=e[h]; if t then if t["dNSName"]then for t,e in d(t["dNSName"])do local t=e:sub(1,2)=="*."; if t then e=e:sub(3);end e=f(o(e)); if e then if t then e="*."..e;end a:set(e,"*",true); end end end if t[i]then for t,e in d(t[i])do e=o(e); if e then a:set(e,"xmpp-client",true); a:set(e,"xmpp-server",true); end end end if t[n]then for t,e in d(t[n])do local t,e=e:match("^_([^.]+)%.(.*)"); if t then e=o(e); if e then a:set(e,t,true); end end end end end local e=s:subject(); for t=1,#e do local e=e[t]; if e.oid==u then local e=o(e.value); if e and r(e)then a:set(e,"*",true); end end end return a.data; end local t="%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. "([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-"; local function a(e) local t,e=e:match(t); if t and e then return c.decode(e),t; end end local i=('.'):rep(64); local o="-----BEGIN %s-----\n%s\n-----END %s-----\n" local function n(t,e) e=e and e:upper()or"CERTIFICATE"; t=c.encode(t); return p(o,e,t:gsub(i,'%0\n',(#t-1)/64),e); end return{ verify_identity=v; get_identities=l; pem2der=a; der2pem=n; }; end) package.preload['verse.bosh']=(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 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 a=5; function verse.new_bosh(a,t) local t={ bosh_conn_pool={}; bosh_waiting_requests={}; bosh_rid=math.random(1,999999); bosh_outgoing_buffer={}; bosh_url=t; conn={}; }; function t:reopen() self.bosh_need_restart=true; self:flush(); end local t=verse.new(a,t); return setmetatable(t,e); end function e:connect() self:_send_session_request(); end function e:send(e) self:debug("Putting into BOSH send buffer: %s",tostring(e)); self.bosh_outgoing_buffer[#self.bosh_outgoing_buffer+1]=i.clone(e); self:flush(); end function e:flush() if self.connected and#self.bosh_waiting_requests<self.bosh_max_requests and(#self.bosh_waiting_requests==0 or#self.bosh_outgoing_buffer>0 or self.bosh_need_restart)then self:debug("Flushing..."); local t=self:_make_body(); local e=self.bosh_outgoing_buffer; for o,a in ipairs(e)do t:add_child(a); e[o]=nil; end self:_make_request(t); else self:debug("Decided not to flush."); end end 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,t); end local e=os.time(); if not self.inactive_since then self.inactive_since=e; elseif e-self.inactive_since>self.bosh_max_inactivity then return self:_disconnected(); else self:debug("%d seconds left to reconnect, retrying in %d seconds...", self.bosh_max_inactivity-(e-self.inactive_since),a); end timer.add_task(a,function() self:debug("Retrying request..."); 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(i); end); end); if e then table.insert(self.bosh_waiting_requests,e); else self:warn("Request failed instantly: %s",t); end end function e:_disconnected() self.connected=nil; self:event("disconnected"); end function e:_send_session_request() local e=self:_make_body(); e.attr.hold="1"; e.attr.wait="60"; e.attr["xml:lang"]="en"; e.attr.ver="1.6"; e.attr.from=self.jid; e.attr.to=self.host; e.attr.secure='true'; o.request(self.bosh_url,{body=tostring(e)},function(e,t) if t==0 then return self:_disconnected(); end local e=self:_parse_response(e) if not e then self:warn("Invalid session creation response"); self:_disconnected(); return; end self.bosh_sid=e.attr.sid; self.bosh_wait=tonumber(e.attr.wait); self.bosh_hold=tonumber(e.attr.hold); self.bosh_max_inactivity=tonumber(e.attr.inactivity); self.bosh_max_requests=tonumber(e.attr.requests)or self.bosh_hold; self.connected=true; self:event("connected"); self:_handle_response_payload(e); end); end function e:_handle_response(t,a,e) if self.bosh_waiting_requests[1]~=e then self:warn("Server replied to request that wasn't the oldest"); for 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); if e then self:_handle_response_payload(e); end self:flush(); end function e:_handle_response_payload(t) local e=t.tags; for t=1,#e do local e=e[t]; if e.attr.xmlns==s then self:event("stream-"..e.name,e); elseif e.attr.xmlns then self:event("stream/"..e.attr.xmlns,e); else self:event("stanza",e); end end if t.attr.type=="terminate"then self:_disconnected({reason=t.attr.condition}); end end local a={ 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; }; function e:_parse_response(e) self:debug("Parsing response: %s",e); if e==nil then self:debug("%s",debug.traceback()); self:_disconnected(); return; end local t={notopen=true,stream=self}; 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; content="text/xml; charset=utf-8"; sid=self.bosh_sid; rid=self.bosh_rid; }); if self.bosh_need_restart then self.bosh_need_restart=nil; e.attr.restart='true'; end return e; 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 i=t.stream_mt; local s=require"util.jid".split; local h=require"net.adns"; local a=require"util.stanza"; local o=require"util.id".short; math.randomseed((require"socket".gettime()*1e6)%2147483648); t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply= a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply; function t.iq(e) if not e.id then e.id=o(); end return a.iq(e); end local r=require"util.xmppstream".new; local n="http://etherx.jabber.org/streams"; local function d(e,t) if e.priority==t.priority then if not e.weight_r then e.weight_r=math.random(); end if not t.weight_r then t.weight_r=math.random(); end return(1+e.weight)*e.weight_r>(1+t.weight)*t.weight_r; end return e.priority<t.priority; end local o={ stream_ns=n, stream_tag="stream", default_ns="jabber:client"}; function o.streamopened(e,t) e.stream_id=t.id; if not e:event("opened",t)then e.notopen=nil; end return true; end function o.streamclosed(e) e.notopen=true; if not e.closed then e:send("</stream:stream>"); e.closed=true; end e:event("closed"); return e:close("stream closed") end function o.handlestanza(t,e) if e.attr.xmlns==n then return t:event("stream-"..e.name,e); elseif e.attr.xmlns then return t:event("stream/"..e.attr.xmlns,e); end return t:event("stanza",e); end function o.error(a,t,e) if a:event(t,e)==nil then if e then local t=e:get_child(nil,"urn:ietf:params:xml:ns:xmpp-streams"); local e=e:get_child_text("text","urn:ietf:params:xml:ns:xmpp-streams"); error(t.name..(e and": "..e or"")); else error(e and e.name or t or"unknown-error"); end end end function i:reset() if self.stream then self.stream:reset(); else self.stream=r(self,o); end self.notopen=true; return true; end function i:connect_client(e,a,i,o) self.jid,self.password=e,a; self.client_key,self.server_key=i,o; self.username,self.host,self.resource=s(e); self:add_plugin("tls"); self:add_plugin("sasl"); self:add_plugin("bind"); 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]+"," ")); self:close("xml-not-well-formed"); end self:hook("connected",function()self:reopen();end); self:hook("incoming-raw",function(e)return self.data(self.conn,e);end); self:hook("read-timeout",function()self:send(" ");return true;end,-1); self.curr_id=0; self.tracked_iqs={}; self:hook("stanza",function(e) local t,a=e.attr.id,e.attr.type; if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then self.tracked_iqs[t](e); self.tracked_iqs[t]=nil; return true; end end); self:hook("stanza",function(e) 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 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 a=self:event(e.name,e); end end return a; end,-1); self:hook("outgoing",function(e) if e.name then self:event("stanza-out",e); end end); self:hook("stanza-out",function(e) if not e.attr.xmlns then self:event(e.name.."-out",e); end end); local function e() self:event("ready"); end self:hook("session-success",e,-1) self:hook("bind-success",e,-1); local t=self.close; function self:close(e) self.close=t; if not self.closed then self:send("</stream:stream>"); self.closed=true; else return self:close(e); end end local function t() self:connect(self.connect_host or self.host,self.connect_port or 5222); end if not(self.connect_host or self.connect_port)then h.lookup(function(a) if a then local e={}; self.srv_hosts=e; for a,t in ipairs(a)do table.insert(e,t.srv); end table.sort(e,d); local a=e[1]; self.srv_choice=1; if a then self.connect_host,self.connect_port=a.target,a.port; self:debug("Best record found, will connect to %s:%d",self.connect_host or self.host,self.connect_port or 5222); end self:hook("disconnected",function() if self.srv_hosts and self.srv_choice<#self.srv_hosts then self.srv_choice=self.srv_choice+1; local e=e[self.srv_choice]; self.connect_host,self.connect_port=e.target,e.port; t(); return true; end end,1e3); self:hook("connected",function() self.srv_hosts=nil; end,1e3); end t(); end,"_xmpp-client._tcp."..(self.host)..".","SRV"); else t(); end end function i:reopen() self:reset(); self:send(a.stanza("stream:stream",{to=self.host,["xmlns:stream"]='http://etherx.jabber.org/streams', xmlns="jabber:client",version="1.0",["xml:lang"]=self.lang}):top_tag()); end function i:send_iq(e,a) local t=e.attr.id or uuid.generate(); self.tracked_iqs[t]=a; e.attr.id=t; self:send(e); end end) package.preload['verse.component']=(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 s=t.stream_mt; local r=require"util.jid".split; local a=require"util.stanza"; local h=require"util.hashes".sha1; t.message,t.presence,t.iq,t.stanza,t.reply,t.error_reply= a.message,a.presence,a.iq,a.stanza,a.reply,a.error_reply; local d=require"util.xmppstream".new; local n="http://etherx.jabber.org/streams"; local o="jabber:component:accept"; local i={ stream_ns=n, stream_tag="stream", default_ns=o}; function i.streamopened(e,t) e.stream_id=t.id; if not e:event("opened",t)then e.notopen=nil; end return true; end function i.streamclosed(e) return e:event("closed"); end function i.handlestanza(t,e) if e.attr.xmlns==n then return t:event("stream-"..e.name,e); elseif e.attr.xmlns or e.name=="handshake"then return t:event("stream/"..(e.attr.xmlns or o),e); end return t:event("stanza",e); end function s:connect_component(e,n) self.jid,self.password=e,n; self.username,self.host,self.resource=r(e); function self:reset() if self.stream then self.stream:reset(); else self.stream=d(self,i); end self.notopen=true; return true; end function self:reopen() self:reset(); self:send(a.stanza("stream:stream",{to=self.jid,["xmlns:stream"]='http://etherx.jabber.org/streams', xmlns=o,version="1.0"}):top_tag()); end function self:close(e) if not self.notopen then self:send("</stream:stream>"); end local t=self.conn.disconnect(); self.conn:close(); t(conn,e); end function self:send_iq(t,a) local e=self:new_id(); self.tracked_iqs[e]=a; t.attr.id=e; self:send(t); end function self:new_id() self.curr_id=self.curr_id+1; return tostring(self.curr_id); end function self.data(t,e) local a,t=self.stream:feed(e); if a then return;end stream:debug("Received invalid XML (%s) %d bytes: %s",tostring(t),#e,e:sub(1,300):gsub("[\r\n]+"," ")); stream:close("xml-not-well-formed"); end self:hook("incoming-raw",function(e)return self.data(self.conn,e);end); self.curr_id=0; self.tracked_iqs={}; self:hook("stanza",function(e) local t,a=e.attr.id,e.attr.type; if t and e.name=="iq"and(a=="result"or a=="error")and self.tracked_iqs[t]then self.tracked_iqs[t](e); self.tracked_iqs[t]=nil; return true; end end); self:hook("stanza",function(e) 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 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 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=h(self.stream_id..n,true); self:send(a.stanza("handshake",{xmlns=o}):text(e)); self:hook("stream/"..o,function(e) if e.name=="handshake"then self:event("authentication-success"); end end); end); local function e() self:event("ready"); end self:hook("authentication-success",e,-1); self:connect(self.connect_host or self.host,self.connect_port or 5347); self:reopen(); end end) pcall(require,"luarocks.require"); local h=require"socket"; pcall(require,"ssl"); local a=require"net.server"; local s=require"util.events"; local o=require"util.logger"; local n=require"util.format".format; 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 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; end local i=0; function e.new(a,o) local t=setmetatable(o or{},t); i=i+1; t.id=tostring(i); t.logger=a or e.new_logger("stream"..t.id); t.events=s.new(); t.plugins={}; t.verse=e; return t; end e.add_task=require"util.timer".add_task; e.logger=o.init; e.new_logger=o.init; e.log=e.logger("verse"); function e.set_log_handler(e,t) t=t or{"debug","info","warn","error"}; o.reset(); if io.type(e)=="file"then local o=e; function e(a,t,e) o:write(a,"\t",t,"\t",e,"\n"); end end if e then local function i(o,a,t,...) return e(o,a,n(t,...)); end for t,e in ipairs(t)do o.add_level_sink(e,i); end end end function e._default_log_handler(o,t,a) return io.stderr:write(o,"\t",t,"\t",a,"\n"); end e.set_log_handler(e._default_log_handler,{"error"}); local function o(t) e.log("error","Error: %s",t); e.log("error","Traceback: %s",debug.traceback()); end function e.set_error_handler(e) o=e; end function e.loop() return xpcall(a.loop,o); end function e.step() return xpcall(a.step,o); end function e.quit() return a.setquitting("once"); end function e.tls_builder(...) return a.tls_builder(...); end function t:listen(t,o) t=t or"localhost"; o=o or 0; local e,a=a.addserver(t,o,e.new_listener(self,"server"),"*a"); if e then self:debug("Bound to %s:%s",t,o); self.server=e; end return e,a; end function t:connect(o,i) o=o or"localhost"; i=tonumber(i)or 5222; local n=h.tcp() n:settimeout(0); n:setoption("keepalive",true); local s,t=n:connect(o,i); if not s and t~="timeout"then self:warn("connect() to %s:%d failed: %s",o,i,t); return self:event("disconnected",{reason=t})or false,t; end local e=a.wrapclient(n,o,i,e.new_listener(self),"*a"); if not e then self:warn("connection initialisation failed: %s",t); return self:event("disconnected",{reason=t})or false,t; end self:set_conn(e); return true; end function t:set_conn(t) self.conn=t; self.send=function(a,e) self:event("outgoing",e); e=tostring(e); self:event("outgoing-raw",e); return t:write(e); end; end function t:close(t) if not self.conn then e.log("error","Attempt to close disconnected connection - possibly a bug"); return; end local e=self.conn.disconnect(); self:event("shutdown"); self.conn:close(); e(self.conn,t); end function t:debug(...) return self.logger("debug",...); end function t:info(...) return self.logger("info",...); end function t:warn(...) return self.logger("warn",...); end function t:error(...) return self.logger("error",...); end function t:event(e,...) self:debug("Firing event: "..tostring(e)); return self.events.fire_event(e,...); end function t:hook(e,...) return self.events.add_handler(e,...); end function t:unhook(t,e) return self.events.remove_handler(t,e); end function e.eventable(e) e.events=s.new(); e.hook,e.unhook=t.hook,t.unhook; local t=e.events.fire_event; function e:event(e,...)return t(e,...);end return e; end function t:add_plugin(t) if self.plugins[t]then return true;end if require("verse.plugins."..t)then local e,a=e.plugins[t](self); if e~=false then self:debug("Loaded %s plugin",t); self.plugins[t]=true; else self:warn("Failed to load %s plugin: %s",t,a); end end return self; end function e.new_listener(t) local a={}; function a.onconnect(o) if t.server then local a=e.new(); o:setlistener(e.new_listener(a)); a:set_conn(o); t:event("connected",{client=a}); else t.connected=true; t:event("connected"); end end function a.onincoming(a,e) t:event("incoming-raw",e); end function a.ondisconnect(a,e) if a~=t.conn then return end t.connected=false; t:event("disconnected",{reason=e}); end function a.ondrain(e) t:event("drained"); end function a.onstatus(a,e) t:event("status",e); end function a.onreadtimeout(e) return t:event("read-timeout"); end return a; end return e;